From 52f5759d2d65cf013eff7375dd5e8f947cb4fc5f Mon Sep 17 00:00:00 2001 From: Cameron DeCoster Date: Tue, 30 Jun 2026 15:34:24 -0600 Subject: [PATCH 1/6] Linting/formatting --- test/jasmine/tests/hover_label_test.js | 8918 ++++++++++++------------ 1 file changed, 4629 insertions(+), 4289 deletions(-) diff --git a/test/jasmine/tests/hover_label_test.js b/test/jasmine/tests/hover_label_test.js index b252872decf..f8dca447bc1 100644 --- a/test/jasmine/tests/hover_label_test.js +++ b/test/jasmine/tests/hover_label_test.js @@ -35,7 +35,7 @@ function touch(path, options) { Lib.clearThrottle(); touchEvent('touchstart', path[0][0], path[0][1], options); - path.slice(1, len).forEach(function(pt) { + path.slice(1, len).forEach(function (pt) { Lib.clearThrottle(); touchEvent('touchmove', pt[0], pt[1], options); }); @@ -44,41 +44,41 @@ function touch(path, options) { return; } -describe('Fx.hover:', function() { +describe('Fx.hover:', function () { var gd; - beforeEach(function() { gd = createGraphDiv(); }); + beforeEach(function () { + gd = createGraphDiv(); + }); afterEach(destroyGraphDiv); - it('should warn when passing subplot ids that are not part of the graph', function(done) { + it('should warn when passing subplot ids that are not part of the graph', function (done) { spyOn(Lib, 'warn'); - var data = [ - {y: [1], hoverinfo: 'y'} - ]; + var data = [{ y: [1], hoverinfo: 'y' }]; var layout = { - xaxis: {domain: [0, 0.5]}, - xaxis2: {anchor: 'y2', domain: [0.5, 1]}, - yaxis2: {anchor: 'x2'}, + xaxis: { domain: [0, 0.5] }, + xaxis2: { anchor: 'y2', domain: [0.5, 1] }, + yaxis2: { anchor: 'x2' }, width: 400, height: 200, - margin: {l: 0, t: 0, r: 0, b: 0}, + margin: { l: 0, t: 0, r: 0, b: 0 }, showlegend: false }; Plotly.newPlot(gd, data, layout) - .then(function() { - Fx.hover(gd, {xpx: 300, ypx: 100}, 'x2y2'); - expect(gd._hoverdata).toBe(undefined, 'did not generate hoverdata'); - expect(Lib.warn).toHaveBeenCalledWith('Unrecognized subplot: x2y2'); - }) - .then(done, done.fail); + .then(function () { + Fx.hover(gd, { xpx: 300, ypx: 100 }, 'x2y2'); + expect(gd._hoverdata).toBe(undefined, 'did not generate hoverdata'); + expect(Lib.warn).toHaveBeenCalledWith('Unrecognized subplot: x2y2'); + }) + .then(done, done.fail); }); }); -describe('hover info', function() { +describe('hover info', function () { 'use strict'; var mock = require('../../image/mocks/14.json'); @@ -86,14 +86,14 @@ describe('hover info', function() { afterEach(destroyGraphDiv); - describe('hover info', function() { + describe('hover info', function () { var mockCopy = Lib.extendDeep({}, mock); - beforeEach(function(done) { + beforeEach(function (done) { Plotly.newPlot(createGraphDiv(), mockCopy.data, mockCopy.layout).then(done); }); - it('responds to hover', function() { + it('responds to hover', function () { var gd = document.getElementById('graph'); Fx.hover('graph', evt, 'xy'); @@ -111,16 +111,16 @@ describe('hover info', function() { }); }); - describe('hover info x', function() { + describe('hover info x', function () { var mockCopy = Lib.extendDeep({}, mock); mockCopy.data[0].hoverinfo = 'x'; - beforeEach(function(done) { + beforeEach(function (done) { Plotly.newPlot(createGraphDiv(), mockCopy.data, mockCopy.layout).then(done); }); - it('responds to hover x', function() { + it('responds to hover x', function () { var gd = document.getElementById('graph'); Fx.hover('graph', evt, 'xy'); @@ -131,20 +131,20 @@ describe('hover info', function() { expect(hoverTrace.x).toEqual(0.388); expect(hoverTrace.y).toEqual(1); - assertHoverLabelContent({axis: '0.388'}); + assertHoverLabelContent({ axis: '0.388' }); }); }); - describe('hover info y', function() { + describe('hover info y', function () { var mockCopy = Lib.extendDeep({}, mock); mockCopy.data[0].hoverinfo = 'y'; - beforeEach(function(done) { + beforeEach(function (done) { Plotly.newPlot(createGraphDiv(), mockCopy.data, mockCopy.layout).then(done); }); - it('responds to hover y', function() { + it('responds to hover y', function () { var gd = document.getElementById('graph'); Fx.hover('graph', evt, 'xy'); @@ -155,11 +155,11 @@ describe('hover info', function() { expect(hoverTrace.x).toEqual(0.388); expect(hoverTrace.y).toEqual(1); - assertHoverLabelContent({nums: '1'}); + assertHoverLabelContent({ nums: '1' }); }); }); - describe('hover info text', function() { + describe('hover info text', function () { var mockCopy = Lib.extendDeep({}, mock); mockCopy.data[0].text = []; @@ -168,11 +168,11 @@ describe('hover info', function() { mockCopy.data[0].text[17] = 'hover\ntext\n\rwith\r\nspaces\n\nnot\rnewlines'; mockCopy.data[0].hoverinfo = 'text'; - beforeEach(function(done) { + beforeEach(function (done) { Plotly.newPlot(createGraphDiv(), mockCopy.data, mockCopy.layout).then(done); }); - it('responds to hover text', function() { + it('responds to hover text', function () { var gd = document.getElementById('graph'); Fx.hover('graph', evt, 'xy'); @@ -189,7 +189,7 @@ describe('hover info', function() { }); }); - describe('hover info text with 0', function() { + describe('hover info text with 0', function () { var mockCopy = Lib.extendDeep({}, mock); mockCopy.data[0].text = []; @@ -199,11 +199,11 @@ describe('hover info', function() { mockCopy.data[0].hoverinfo = 'text'; mockCopy.data[0].mode = 'lines+markers+text'; - beforeEach(function(done) { + beforeEach(function (done) { Plotly.newPlot(createGraphDiv(), mockCopy.data, mockCopy.layout).then(done); }); - it('responds to hover text', function() { + it('responds to hover text', function () { var gd = document.getElementById('graph'); Fx.hover('graph', evt, 'xy'); @@ -219,7 +219,7 @@ describe('hover info', function() { expect(txs.size()).toBe(1); - txs.each(function() { + txs.each(function () { expect(d3Select(this).text()).toBe('0'); }); @@ -229,18 +229,18 @@ describe('hover info', function() { }); }); - describe('hover info all', function() { + describe('hover info all', function () { var mockCopy = Lib.extendDeep({}, mock); mockCopy.data[0].text = []; mockCopy.data[0].text[17] = 'hover text'; mockCopy.data[0].hoverinfo = 'all'; - beforeEach(function(done) { + beforeEach(function (done) { Plotly.newPlot(createGraphDiv(), mockCopy.data, mockCopy.layout).then(done); }); - it('responds to hover all', function() { + it('responds to hover all', function () { var gd = document.getElementById('graph'); Fx.hover('graph', evt, 'xy'); @@ -259,7 +259,7 @@ describe('hover info', function() { }); }); - describe('hover info with bad name', function() { + describe('hover info with bad name', function () { var mockCopy = Lib.extendDeep({}, mock); mockCopy.data[0].text = []; @@ -273,11 +273,11 @@ describe('hover info', function() { name: 'another trace' }); - beforeEach(function(done) { + beforeEach(function (done) { Plotly.newPlot(createGraphDiv(), mockCopy.data, mockCopy.layout).then(done); }); - it('cleans the name', function() { + it('cleans the name', function () { var gd = document.getElementById('graph'); Fx.hover('graph', evt, 'xy'); @@ -296,7 +296,7 @@ describe('hover info', function() { }); }); - describe('hover info y on log axis', function() { + describe('hover info y on log axis', function () { var mockCopy = Lib.extendDeep({}, mock); mockCopy.data[0].hoverinfo = 'y'; @@ -304,33 +304,33 @@ describe('hover info', function() { // out all the tspan stuff here. mockCopy.layout.yaxis.exponentformat = 'e'; - beforeEach(function(done) { - for(var i = 0; i < mockCopy.data[0].y.length; i++) { + beforeEach(function (done) { + for (var i = 0; i < mockCopy.data[0].y.length; i++) { mockCopy.data[0].y[i] *= 1e9; } Plotly.newPlot(createGraphDiv(), mockCopy.data, mockCopy.layout).then(done); }); - it('responds to hover y', function() { + it('responds to hover y', function () { Fx.hover('graph', evt, 'xy'); - assertHoverLabelContent({nums: '1e+9'}); + assertHoverLabelContent({ nums: '1e+9' }); }); }); - describe('hover info y+text', function() { + describe('hover info y+text', function () { var mockCopy = Lib.extendDeep({}, mock); mockCopy.data[0].text = []; mockCopy.data[0].text[17] = 'hover text'; mockCopy.data[0].hoverinfo = 'y+text'; - beforeEach(function(done) { + beforeEach(function (done) { Plotly.newPlot(createGraphDiv(), mockCopy.data, mockCopy.layout).then(done); }); - it('responds to hover y+text', function() { + it('responds to hover y+text', function () { var gd = document.getElementById('graph'); Fx.hover('graph', evt, 'xy'); @@ -347,18 +347,18 @@ describe('hover info', function() { }); }); - describe('hover info x+text', function() { + describe('hover info x+text', function () { var mockCopy = Lib.extendDeep({}, mock); mockCopy.data[0].text = []; mockCopy.data[0].text[17] = 'hover text'; mockCopy.data[0].hoverinfo = 'x+text'; - beforeEach(function(done) { + beforeEach(function (done) { Plotly.newPlot(createGraphDiv(), mockCopy.data, mockCopy.layout).then(done); }); - it('responds to hover x+text', function() { + it('responds to hover x+text', function () { var gd = document.getElementById('graph'); Fx.hover('graph', evt, 'xy'); @@ -376,17 +376,17 @@ describe('hover info', function() { }); }); - describe('hover error x text (log axis positive)', function() { + describe('hover error x text (log axis positive)', function () { var mockCopy = Lib.extendDeep({}, mock); mockCopy.data[0].error_x = { array: [] }; mockCopy.data[0].error_x.array[17] = 1; - beforeEach(function(done) { + beforeEach(function (done) { Plotly.newPlot(createGraphDiv(), mockCopy.data, mockCopy.layout).then(done); }); - it('responds to hover x+text', function() { + it('responds to hover x+text', function () { Fx.hover('graph', evt, 'xy'); assertHoverLabelContent({ @@ -396,17 +396,17 @@ describe('hover info', function() { }); }); - describe('hover error text (log axis 0)', function() { + describe('hover error text (log axis 0)', function () { var mockCopy = Lib.extendDeep({}, mock); mockCopy.data[0].error_x = { array: [] }; mockCopy.data[0].error_x.array[17] = 0; - beforeEach(function(done) { + beforeEach(function (done) { Plotly.newPlot(createGraphDiv(), mockCopy.data, mockCopy.layout).then(done); }); - it('responds to hover x+text', function() { + it('responds to hover x+text', function () { Fx.hover('graph', evt, 'xy'); assertHoverLabelContent({ @@ -416,17 +416,17 @@ describe('hover info', function() { }); }); - describe('hover error text (log axis negative)', function() { + describe('hover error text (log axis negative)', function () { var mockCopy = Lib.extendDeep({}, mock); mockCopy.data[0].error_x = { array: [] }; mockCopy.data[0].error_x.array[17] = -1; - beforeEach(function(done) { + beforeEach(function (done) { Plotly.newPlot(createGraphDiv(), mockCopy.data, mockCopy.layout).then(done); }); - it('responds to hover x+text', function() { + it('responds to hover x+text', function () { Fx.hover('graph', evt, 'xy'); assertHoverLabelContent({ @@ -436,18 +436,18 @@ describe('hover info', function() { }); }); - describe('hover info text with html', function() { + describe('hover info text with html', function () { var mockCopy = Lib.extendDeep({}, mock); mockCopy.data[0].text = []; mockCopy.data[0].text[17] = 'hover
text'; mockCopy.data[0].hoverinfo = 'text'; - beforeEach(function(done) { + beforeEach(function (done) { Plotly.newPlot(createGraphDiv(), mockCopy.data, mockCopy.layout).then(done); }); - it('responds to hover text with html', function() { + it('responds to hover text with html', function () { var gd = document.getElementById('graph'); Fx.hover('graph', evt, 'xy'); @@ -464,16 +464,16 @@ describe('hover info', function() { }); }); - describe('hover info skip', function() { + describe('hover info skip', function () { var mockCopy = Lib.extendDeep({}, mock); mockCopy.data[0].hoverinfo = 'skip'; - beforeEach(function(done) { + beforeEach(function (done) { Plotly.newPlot(createGraphDiv(), mockCopy.data, mockCopy.layout).then(done); }); - it('does not hover if hover info is set to skip', function() { + it('does not hover if hover info is set to skip', function () { var gd = document.getElementById('graph'); Fx.hover('graph', evt, 'xy'); @@ -482,16 +482,16 @@ describe('hover info', function() { }); }); - describe('hover info none', function() { + describe('hover info none', function () { var mockCopy = Lib.extendDeep({}, mock); mockCopy.data[0].hoverinfo = 'none'; - beforeEach(function(done) { + beforeEach(function (done) { Plotly.newPlot(createGraphDiv(), mockCopy.data, mockCopy.layout).then(done); }); - it('does not render if hover is set to none', function() { + it('does not render if hover is set to none', function () { var gd = document.getElementById('graph'); Fx.hover('graph', evt, 'xy'); @@ -506,7 +506,7 @@ describe('hover info', function() { }); }); - describe('\'closest\' hover info (superimposed case)', function() { + describe("'closest' hover info (superimposed case)", function () { var mockCopy = Lib.extendDeep({}, mock); // superimposed traces @@ -515,12 +515,12 @@ describe('hover info', function() { var gd; - beforeEach(function(done) { + beforeEach(function (done) { gd = createGraphDiv(); Plotly.newPlot(gd, mockCopy.data, mockCopy.layout).then(done); }); - it('render hover labels of the above trace', function() { + it('render hover labels of the above trace', function () { Fx.hover('graph', evt, 'xy'); expect(gd._hoverdata.length).toEqual(1); @@ -539,25 +539,26 @@ describe('hover info', function() { }); }); - it('render only non-hoverinfo \'none\' hover labels', function(done) { - Plotly.restyle(gd, 'hoverinfo', ['none', 'name']).then(function() { - Fx.hover('graph', evt, 'xy'); + it("render only non-hoverinfo 'none' hover labels", function (done) { + Plotly.restyle(gd, 'hoverinfo', ['none', 'name']) + .then(function () { + Fx.hover('graph', evt, 'xy'); - expect(gd._hoverdata.length).toEqual(1); + expect(gd._hoverdata.length).toEqual(1); - var hoverTrace = gd._hoverdata[0]; + var hoverTrace = gd._hoverdata[0]; - expect(hoverTrace.fullData.index).toEqual(1); - expect(hoverTrace.curveNumber).toEqual(1); - expect(hoverTrace.pointNumber).toEqual(16); - expect(hoverTrace.x).toEqual(0.33); - expect(hoverTrace.y).toEqual(1.25); + expect(hoverTrace.fullData.index).toEqual(1); + expect(hoverTrace.curveNumber).toEqual(1); + expect(hoverTrace.pointNumber).toEqual(16); + expect(hoverTrace.x).toEqual(0.33); + expect(hoverTrace.y).toEqual(1.25); - assertHoverLabelContent({ - nums: 'PV learning ...' - }); - }) - .then(done, done.fail); + assertHoverLabelContent({ + nums: 'PV learning ...' + }); + }) + .then(done, done.fail); }); }); @@ -572,926 +573,1099 @@ describe('hover info', function() { var clientX = xpx + gdBB.left + gd._fullLayout._size.l; var clientY = ypx + gdBB.top + gd._fullLayout._size.t; - Fx.hover(gd, { clientX: clientX, clientY: clientY, target: dragger}, 'xy'); + Fx.hover(gd, { clientX: clientX, clientY: clientY, target: dragger }, 'xy'); Lib.clearThrottle(); } - describe('hover label order for stacked traces with zeros', function() { + describe('hover label order for stacked traces with zeros', function () { var gd; - beforeEach(function() { + beforeEach(function () { gd = createGraphDiv(); }); - it('puts the top trace on top', function(done) { - Plotly.newPlot(gd, [ - {y: [1, 2, 3], type: 'bar', name: 'a'}, - {y: [2, 0, 1], type: 'bar', name: 'b'}, - {y: [1, 0, 1], type: 'bar', name: 'c'}, - {y: [2, 1, 0], type: 'bar', name: 'd'} - ], { - hovermode: 'x', - width: 500, - height: 400, - margin: {l: 0, t: 0, r: 0, b: 0}, - barmode: 'stack' - }) - .then(function() { - _hover(gd, 250, 250); - assertHoverLabelContent({ - nums: ['2', '0', '0', '1'], - name: ['a', 'b', 'c', 'd'], - // a, b, c are all in the same place but keep their order - // d is included mostly as a sanity check - vOrder: [3, 2, 1, 0], - axis: '1' - }); + it('puts the top trace on top', function (done) { + Plotly.newPlot( + gd, + [ + { y: [1, 2, 3], type: 'bar', name: 'a' }, + { y: [2, 0, 1], type: 'bar', name: 'b' }, + { y: [1, 0, 1], type: 'bar', name: 'c' }, + { y: [2, 1, 0], type: 'bar', name: 'd' } + ], + { + hovermode: 'x', + width: 500, + height: 400, + margin: { l: 0, t: 0, r: 0, b: 0 }, + barmode: 'stack' + } + ) + .then(function () { + _hover(gd, 250, 250); + assertHoverLabelContent({ + nums: ['2', '0', '0', '1'], + name: ['a', 'b', 'c', 'd'], + // a, b, c are all in the same place but keep their order + // d is included mostly as a sanity check + vOrder: [3, 2, 1, 0], + axis: '1' + }); - // reverse the axis, labels should reverse - return Plotly.relayout(gd, 'yaxis.range', gd.layout.yaxis.range.slice().reverse()); - }) - .then(function() { - _hover(gd, 250, 250); - assertHoverLabelContent({ - nums: ['2', '0', '0', '1'], - name: ['a', 'b', 'c', 'd'], - vOrder: [0, 1, 2, 3], - axis: '1' - }); - }) - .then(done, done.fail); + // reverse the axis, labels should reverse + return Plotly.relayout(gd, 'yaxis.range', gd.layout.yaxis.range.slice().reverse()); + }) + .then(function () { + _hover(gd, 250, 250); + assertHoverLabelContent({ + nums: ['2', '0', '0', '1'], + name: ['a', 'b', 'c', 'd'], + vOrder: [0, 1, 2, 3], + axis: '1' + }); + }) + .then(done, done.fail); }); - it('puts the right trace on the right', function(done) { - Plotly.newPlot(gd, [ - {x: [1, 2, 3], type: 'bar', name: 'a', orientation: 'h'}, - {x: [2, 0, 1], type: 'bar', name: 'b', orientation: 'h'}, - {x: [1, 0, 1], type: 'bar', name: 'c', orientation: 'h'}, - {x: [2, 1, 0], type: 'bar', name: 'd', orientation: 'h'} - ], { - hovermode: 'y', - width: 500, - height: 400, - margin: {l: 0, t: 0, r: 0, b: 0}, - barmode: 'stack' - }) - .then(function() { - _hover(gd, 250, 250); - assertHoverLabelContent({ - nums: ['2', '0', '0', '1'], - name: ['a', 'b', 'c', 'd'], - // a, b, c are all in the same place but keep their order - // d is included mostly as a sanity check - hOrder: [3, 2, 1, 0], - axis: '1' - }); + it('puts the right trace on the right', function (done) { + Plotly.newPlot( + gd, + [ + { x: [1, 2, 3], type: 'bar', name: 'a', orientation: 'h' }, + { x: [2, 0, 1], type: 'bar', name: 'b', orientation: 'h' }, + { x: [1, 0, 1], type: 'bar', name: 'c', orientation: 'h' }, + { x: [2, 1, 0], type: 'bar', name: 'd', orientation: 'h' } + ], + { + hovermode: 'y', + width: 500, + height: 400, + margin: { l: 0, t: 0, r: 0, b: 0 }, + barmode: 'stack' + } + ) + .then(function () { + _hover(gd, 250, 250); + assertHoverLabelContent({ + nums: ['2', '0', '0', '1'], + name: ['a', 'b', 'c', 'd'], + // a, b, c are all in the same place but keep their order + // d is included mostly as a sanity check + hOrder: [3, 2, 1, 0], + axis: '1' + }); - // reverse the axis, labels should reverse - return Plotly.relayout(gd, 'xaxis.range', gd.layout.xaxis.range.slice().reverse()); - }) - .then(function() { - _hover(gd, 250, 250); - assertHoverLabelContent({ - nums: ['2', '0', '0', '1'], - name: ['a', 'b', 'c', 'd'], - hOrder: [0, 1, 2, 3], - axis: '1' - }); - }) - .then(done, done.fail); + // reverse the axis, labels should reverse + return Plotly.relayout(gd, 'xaxis.range', gd.layout.xaxis.range.slice().reverse()); + }) + .then(function () { + _hover(gd, 250, 250); + assertHoverLabelContent({ + nums: ['2', '0', '0', '1'], + name: ['a', 'b', 'c', 'd'], + hOrder: [0, 1, 2, 3], + axis: '1' + }); + }) + .then(done, done.fail); }); }); - describe('hover info for x/y/z traces', function() { + describe('hover info for x/y/z traces', function () { var gd; - beforeEach(function() { + beforeEach(function () { gd = createGraphDiv(); }); - it('should display correct label content', function(done) { - Plotly.newPlot(gd, [{ - type: 'heatmap', - y: [0, 1], - z: [[1, 2, 3], [2, 2, 1]], - name: 'one', - }, { - type: 'heatmap', - y: [2, 3], - z: [[1, 2, 3], [2, 2, 1]], - name: 'two' - }], { - width: 500, - height: 400, - margin: {l: 0, t: 0, r: 0, b: 0} - }) - .then(function() { - _hover(gd, 250, 100); - assertHoverLabelContent({ - nums: 'x: 1\ny: 3\nz: 2', - name: 'two' - }); + it('should display correct label content', function (done) { + Plotly.newPlot( + gd, + [ + { + type: 'heatmap', + y: [0, 1], + z: [ + [1, 2, 3], + [2, 2, 1] + ], + name: 'one' + }, + { + type: 'heatmap', + y: [2, 3], + z: [ + [1, 2, 3], + [2, 2, 1] + ], + name: 'two' + } + ], + { + width: 500, + height: 400, + margin: { l: 0, t: 0, r: 0, b: 0 } + } + ) + .then(function () { + _hover(gd, 250, 100); + assertHoverLabelContent({ + nums: 'x: 1\ny: 3\nz: 2', + name: 'two' + }); - _hover(gd, 250, 300); - assertHoverLabelContent({ - nums: 'x: 1\ny: 1\nz: 2', - name: 'one' - }); - }) - .then(function() { - return Plotly.restyle(gd, 'hovertext', [ - [['A', 'B', 'C'], ['X', 'Y', 'Z']] - ], [1]); - }) - .then(function() { - _hover(gd, 250, 100); - assertHoverLabelContent({ - nums: 'x: 1\ny: 3\nz: 2\nY', - name: 'two' - }); - }) - .then(done, done.fail); + _hover(gd, 250, 300); + assertHoverLabelContent({ + nums: 'x: 1\ny: 1\nz: 2', + name: 'one' + }); + }) + .then(function () { + return Plotly.restyle( + gd, + 'hovertext', + [ + [ + ['A', 'B', 'C'], + ['X', 'Y', 'Z'] + ] + ], + [1] + ); + }) + .then(function () { + _hover(gd, 250, 100); + assertHoverLabelContent({ + nums: 'x: 1\ny: 3\nz: 2\nY', + name: 'two' + }); + }) + .then(done, done.fail); }); - it('should display correct label content with specified format - heatmap', function(done) { - Plotly.newPlot(gd, [{ - type: 'heatmap', - y: [0, 1], - z: [[1.11111, 2.2222, 3.33333], [4.44444, 5.55555, 6.66666]], - name: 'one', - zhoverformat: '.2f', - showscale: false - }, { - type: 'heatmap', - y: [2, 3], - z: [[1, 2, 3], [2, 2, 1]], - name: 'two', - showscale: false - }], { - width: 500, - height: 400, - margin: {l: 0, t: 0, r: 0, b: 0} - }) - .then(function() { - _hover(gd, 250, 100); - assertHoverLabelContent({ - nums: 'x: 1\ny: 3\nz: 2', - name: 'two' - }); + it('should display correct label content with specified format - heatmap', function (done) { + Plotly.newPlot( + gd, + [ + { + type: 'heatmap', + y: [0, 1], + z: [ + [1.11111, 2.2222, 3.33333], + [4.44444, 5.55555, 6.66666] + ], + name: 'one', + zhoverformat: '.2f', + showscale: false + }, + { + type: 'heatmap', + y: [2, 3], + z: [ + [1, 2, 3], + [2, 2, 1] + ], + name: 'two', + showscale: false + } + ], + { + width: 500, + height: 400, + margin: { l: 0, t: 0, r: 0, b: 0 } + } + ) + .then(function () { + _hover(gd, 250, 100); + assertHoverLabelContent({ + nums: 'x: 1\ny: 3\nz: 2', + name: 'two' + }); - _hover(gd, 250, 300); - assertHoverLabelContent({ - nums: 'x: 1\ny: 1\nz: 5.56', - name: 'one' - }); - }) - .then(done, done.fail); + _hover(gd, 250, 300); + assertHoverLabelContent({ + nums: 'x: 1\ny: 1\nz: 5.56', + name: 'one' + }); + }) + .then(done, done.fail); }); - it('provides exponents correctly for z data', function(done) { + it('provides exponents correctly for z data', function (done) { function expFmt(val, exp) { - return val + '×10\u200b' + + return ( + val + + '×10\u200b' + (exp < 0 ? MINUS_SIGN + -exp : exp) + - '\u200b'; + '\u200b' + ); } - Plotly.newPlot(gd, [{ - type: 'heatmap', - y: [0, 1, 2, 3], - z: [ - [-1.23456789e23, -1e10, -1e4], - [-1e-2, -1e-8, 0], - [1.23456789e-23, 1e-8, 1e-2], - [123.456789, 1.23456789e10, 1e23] - ], - showscale: false - }], { - width: 600, - height: 400, - margin: {l: 0, t: 0, r: 0, b: 0} - }) - .then(function() { + Plotly.newPlot( + gd, [ - [expFmt(MINUS_SIGN + '1.234568', 23), MINUS_SIGN + '10B', MINUS_SIGN + '10k'], - [MINUS_SIGN + '0.01', MINUS_SIGN + '10n', '0'], - [expFmt('1.234568', -23), '10n', '0.01'], - ['123.4568', '12.34568B', expFmt('1', 23)] - ] - .forEach(function(row, y) { - row.forEach(function(zVal, x) { - _hover(gd, (x + 0.5) * 200, (3.5 - y) * 100); - assertHoverLabelContent({nums: 'x: ' + x + '\ny: ' + y + '\nz: ' + zVal}, zVal); + { + type: 'heatmap', + y: [0, 1, 2, 3], + z: [ + [-1.23456789e23, -1e10, -1e4], + [-1e-2, -1e-8, 0], + [1.23456789e-23, 1e-8, 1e-2], + [123.456789, 1.23456789e10, 1e23] + ], + showscale: false + } + ], + { + width: 600, + height: 400, + margin: { l: 0, t: 0, r: 0, b: 0 } + } + ) + .then(function () { + [ + [expFmt(MINUS_SIGN + '1.234568', 23), MINUS_SIGN + '10B', MINUS_SIGN + '10k'], + [MINUS_SIGN + '0.01', MINUS_SIGN + '10n', '0'], + [expFmt('1.234568', -23), '10n', '0.01'], + ['123.4568', '12.34568B', expFmt('1', 23)] + ].forEach(function (row, y) { + row.forEach(function (zVal, x) { + _hover(gd, (x + 0.5) * 200, (3.5 - y) * 100); + assertHoverLabelContent({ nums: 'x: ' + x + '\ny: ' + y + '\nz: ' + zVal }, zVal); + }); }); - }); - }) - .then(done, done.fail); + }) + .then(done, done.fail); }); - it('should display correct label content with specified format - contour', function(done) { - Plotly.newPlot(gd, [{ - type: 'contour', - y: [0, 1], - z: [[1.11111, 2.2222, 3.33333], [4.44444, 5.55555, 6.66666]], - name: 'one', - zhoverformat: '.2f', - showscale: false - }, { - type: 'contour', - y: [2, 3], - z: [[1, 2, 3], [2, 2, 1]], - name: 'two', - showscale: false - }], { - width: 500, - height: 400, - margin: {l: 0, t: 0, r: 0, b: 0} - }) - .then(function() { - _hover(gd, 250, 50); - assertHoverLabelContent({ - nums: 'x: 1\ny: 3\nz: 2', - name: 'two' - }); + it('should display correct label content with specified format - contour', function (done) { + Plotly.newPlot( + gd, + [ + { + type: 'contour', + y: [0, 1], + z: [ + [1.11111, 2.2222, 3.33333], + [4.44444, 5.55555, 6.66666] + ], + name: 'one', + zhoverformat: '.2f', + showscale: false + }, + { + type: 'contour', + y: [2, 3], + z: [ + [1, 2, 3], + [2, 2, 1] + ], + name: 'two', + showscale: false + } + ], + { + width: 500, + height: 400, + margin: { l: 0, t: 0, r: 0, b: 0 } + } + ) + .then(function () { + _hover(gd, 250, 50); + assertHoverLabelContent({ + nums: 'x: 1\ny: 3\nz: 2', + name: 'two' + }); - _hover(gd, 250, 300); - assertHoverLabelContent({ - nums: 'x: 1\ny: 1\nz: 5.56', - name: 'one' - }); - }) - .then(function() { - return Plotly.restyle(gd, 'hovertext', [ - [['A', 'B', 'C'], ['X', 'Y', 'Z']] - ]); - }) - .then(function() { - _hover(gd, 250, 50); - assertHoverLabelContent({ - nums: 'x: 1\ny: 3\nz: 2\nY', - name: 'two' - }); - }) - .then(function() { - return Plotly.restyle(gd, 'hovertemplate', '(%{x},%{y}) -- %{z}trace %{data.name}'); - }) - .then(function() { - _hover(gd, 250, 50); - assertHoverLabelContent({ - nums: '(1,3) -- 2', - name: 'trace two' - }); + _hover(gd, 250, 300); + assertHoverLabelContent({ + nums: 'x: 1\ny: 1\nz: 5.56', + name: 'one' + }); + }) + .then(function () { + return Plotly.restyle(gd, 'hovertext', [ + [ + ['A', 'B', 'C'], + ['X', 'Y', 'Z'] + ] + ]); + }) + .then(function () { + _hover(gd, 250, 50); + assertHoverLabelContent({ + nums: 'x: 1\ny: 3\nz: 2\nY', + name: 'two' + }); + }) + .then(function () { + return Plotly.restyle(gd, 'hovertemplate', '(%{x},%{y}) -- %{z}trace %{data.name}'); + }) + .then(function () { + _hover(gd, 250, 50); + assertHoverLabelContent({ + nums: '(1,3) -- 2', + name: 'trace two' + }); - _hover(gd, 250, 300); - assertHoverLabelContent({ - nums: '(1,1) -- 5.56', - name: 'trace one' - }); - }) - .then(function() { - return Plotly.restyle(gd, 'hoverlabel.namelength', 2); - }) - .then(function() { - _hover(gd, 250, 50); - assertHoverLabelContent({ - nums: '(1,3) -- 2', - name: 'tr' - }); + _hover(gd, 250, 300); + assertHoverLabelContent({ + nums: '(1,1) -- 5.56', + name: 'trace one' + }); + }) + .then(function () { + return Plotly.restyle(gd, 'hoverlabel.namelength', 2); + }) + .then(function () { + _hover(gd, 250, 50); + assertHoverLabelContent({ + nums: '(1,3) -- 2', + name: 'tr' + }); - _hover(gd, 250, 300); - assertHoverLabelContent({ - nums: '(1,1) -- 5.56', - name: 'tr' - }); - }) - .then(function() { - var nl = [[0, 0, 0], [0, 0, 0]]; - return Plotly.restyle(gd, 'hoverlabel.namelength', [nl, nl]); - }) - .then(function() { - _hover(gd, 250, 50); - assertHoverLabelContent({ - nums: '(1,3) -- 2', - name: '' - }); + _hover(gd, 250, 300); + assertHoverLabelContent({ + nums: '(1,1) -- 5.56', + name: 'tr' + }); + }) + .then(function () { + var nl = [ + [0, 0, 0], + [0, 0, 0] + ]; + return Plotly.restyle(gd, 'hoverlabel.namelength', [nl, nl]); + }) + .then(function () { + _hover(gd, 250, 50); + assertHoverLabelContent({ + nums: '(1,3) -- 2', + name: '' + }); - _hover(gd, 250, 300); - assertHoverLabelContent({ - nums: '(1,1) -- 5.56', - name: '' - }); - }) - .then(function() { - return Plotly.restyle(gd, 'hovertemplate', null); - }) - .then(function() { - _hover(gd, 250, 50); - assertHoverLabelContent({ - nums: 'x: 1\ny: 3\nz: 2\nY', - name: '' - }); + _hover(gd, 250, 300); + assertHoverLabelContent({ + nums: '(1,1) -- 5.56', + name: '' + }); + }) + .then(function () { + return Plotly.restyle(gd, 'hovertemplate', null); + }) + .then(function () { + _hover(gd, 250, 50); + assertHoverLabelContent({ + nums: 'x: 1\ny: 3\nz: 2\nY', + name: '' + }); - _hover(gd, 250, 300); - assertHoverLabelContent({ - nums: 'x: 1\ny: 1\nz: 5.56\nY', - name: '' - }); - }) - .then(done, done.fail); + _hover(gd, 250, 300); + assertHoverLabelContent({ + nums: 'x: 1\ny: 1\nz: 5.56\nY', + name: '' + }); + }) + .then(done, done.fail); }); - it('should get the right content and color for contour constraints', function(done) { + it('should get the right content and color for contour constraints', function (done) { var contourConstraints = require('../../image/mocks/contour_constraints.json'); var fig = Lib.extendDeep({}, contourConstraints); fig.layout.hovermode = 'x'; Plotly.newPlot(gd, fig) - .then(function() { - _hover(gd, 250, 250); - assertHoverLabelContent({ - nums: [ - 'x: 1\ny: 1\nz: 3.00', // custom zhoverformat - 'x: 1\ny: 1\nz: 3', - 'x: 1\ny: 1\nz: 10', - 'x: 1\ny: 1\nz: 10', - 'x: 1\ny: 1\nz: 10', - 'x: 1\ny: 1\nz: 10', - 'x: 1\ny: 1\nz: 10' - ], - name: ['[2, 4]', '=3.0001', '>1', '>0.25', ']6, 7[', '<8', ']3, 4['] - }); - var styles = [{ - // This first one has custom styles. The others all inherit from trace styles. - bgcolor: 'rgb(200, 200, 200)', - bordercolor: 'rgb(0, 0, 100)', - fontSize: 20, - fontFamily: 'Arial', - fontColor: 'rgb(0, 100, 200)' - }, { - bgcolor: 'rgb(227, 119, 194)', - bordercolor: 'rgb(68, 68, 68)', - fontSize: 13, - fontFamily: 'Arial', - fontColor: 'rgb(68, 68, 68)' - }, { - bgcolor: 'rgb(140, 86, 75)', - bordercolor: 'rgb(255, 255, 255)', - fontSize: 13, - fontFamily: 'Arial', - fontColor: 'rgb(255, 255, 255)' - }, { - bgcolor: 'rgb(150, 0, 200)', - bordercolor: 'rgb(255, 255, 255)', - fontSize: 13, - fontFamily: 'Arial', - fontColor: 'rgb(255, 255, 255)' - }, { - bgcolor: 'rgb(150, 0, 0)', - bordercolor: 'rgb(255, 255, 255)', - fontSize: 13, - fontFamily: 'Arial', - fontColor: 'rgb(255, 255, 255)' - }, { - bgcolor: 'rgb(0, 200, 0)', - bordercolor: 'rgb(255, 255, 255)', - fontSize: 13, - fontFamily: 'Arial', - fontColor: 'rgb(255, 255, 255)' - }, { - bgcolor: 'rgb(255, 127, 14)', - bordercolor: 'rgb(68, 68, 68)', - fontSize: 13, - fontFamily: 'Arial', - fontColor: 'rgb(68, 68, 68)' - }]; - d3SelectAll('g.hovertext').each(function(_, i) { - assertHoverLabelStyle(d3Select(this), styles[i]); - }); - }) - .then(done, done.fail); + .then(function () { + _hover(gd, 250, 250); + assertHoverLabelContent({ + nums: [ + 'x: 1\ny: 1\nz: 3.00', // custom zhoverformat + 'x: 1\ny: 1\nz: 3', + 'x: 1\ny: 1\nz: 10', + 'x: 1\ny: 1\nz: 10', + 'x: 1\ny: 1\nz: 10', + 'x: 1\ny: 1\nz: 10', + 'x: 1\ny: 1\nz: 10' + ], + name: ['[2, 4]', '=3.0001', '>1', '>0.25', ']6, 7[', '<8', ']3, 4['] + }); + var styles = [ + { + // This first one has custom styles. The others all inherit from trace styles. + bgcolor: 'rgb(200, 200, 200)', + bordercolor: 'rgb(0, 0, 100)', + fontSize: 20, + fontFamily: 'Arial', + fontColor: 'rgb(0, 100, 200)' + }, + { + bgcolor: 'rgb(227, 119, 194)', + bordercolor: 'rgb(68, 68, 68)', + fontSize: 13, + fontFamily: 'Arial', + fontColor: 'rgb(68, 68, 68)' + }, + { + bgcolor: 'rgb(140, 86, 75)', + bordercolor: 'rgb(255, 255, 255)', + fontSize: 13, + fontFamily: 'Arial', + fontColor: 'rgb(255, 255, 255)' + }, + { + bgcolor: 'rgb(150, 0, 200)', + bordercolor: 'rgb(255, 255, 255)', + fontSize: 13, + fontFamily: 'Arial', + fontColor: 'rgb(255, 255, 255)' + }, + { + bgcolor: 'rgb(150, 0, 0)', + bordercolor: 'rgb(255, 255, 255)', + fontSize: 13, + fontFamily: 'Arial', + fontColor: 'rgb(255, 255, 255)' + }, + { + bgcolor: 'rgb(0, 200, 0)', + bordercolor: 'rgb(255, 255, 255)', + fontSize: 13, + fontFamily: 'Arial', + fontColor: 'rgb(255, 255, 255)' + }, + { + bgcolor: 'rgb(255, 127, 14)', + bordercolor: 'rgb(68, 68, 68)', + fontSize: 13, + fontFamily: 'Arial', + fontColor: 'rgb(68, 68, 68)' + } + ]; + d3SelectAll('g.hovertext').each(function (_, i) { + assertHoverLabelStyle(d3Select(this), styles[i]); + }); + }) + .then(done, done.fail); }); - it('should display correct label content with specified format - histogram2dcontour', function(done) { - Plotly.newPlot(gd, [{ - type: 'histogram2dcontour', - x: [0, 1, 2, 0, 1, 2, 1], - y: [0, 0, 0, 1, 1, 1, 1], - z: [1.11111, 2.2222, 3.3333, 4.4444, 4.4444, 6.6666, 1.1111], - histfunc: 'sum', - name: 'one', - zhoverformat: '.2f', - showscale: false - }, { - type: 'histogram2dcontour', - x: [0, 1, 2, 0, 1, 2, 1, 2, 0, 1, 2], - y: [2, 2, 2, 3, 3, 3, 2, 2, 3, 3, 2], - name: 'two', - showscale: false - }], { - hovermode: 'x', - width: 500, - height: 400, - margin: {l: 0, t: 0, r: 0, b: 0} - }) - .then(function() { - _hover(gd, 250, 50); - assertHoverLabelContent({ - nums: 'x: 1\ny: 3\nz: 2', - name: 'two' - }); - - _hover(gd, 250, 270); - assertHoverLabelContent({ - nums: [ - 'x: 1\ny: 1\nz: 5.56', - 'x: 1\ny: 1\nz: 0' - ], - name: [ - 'one', - 'two' - ] - }); - }) - .then(function() { - return Plotly.restyle(gd, 'hovertemplate', 'f(%{x:.1f}, %{y:.1f})=%{z}'); - }) - .then(function() { - _hover(gd, 250, 50); - assertHoverLabelContent({ - nums: 'f(1.0, 3.0)=2', - name: '' - }); + it('should display correct label content with specified format - histogram2dcontour', function (done) { + Plotly.newPlot( + gd, + [ + { + type: 'histogram2dcontour', + x: [0, 1, 2, 0, 1, 2, 1], + y: [0, 0, 0, 1, 1, 1, 1], + z: [1.11111, 2.2222, 3.3333, 4.4444, 4.4444, 6.6666, 1.1111], + histfunc: 'sum', + name: 'one', + zhoverformat: '.2f', + showscale: false + }, + { + type: 'histogram2dcontour', + x: [0, 1, 2, 0, 1, 2, 1, 2, 0, 1, 2], + y: [2, 2, 2, 3, 3, 3, 2, 2, 3, 3, 2], + name: 'two', + showscale: false + } + ], + { + hovermode: 'x', + width: 500, + height: 400, + margin: { l: 0, t: 0, r: 0, b: 0 } + } + ) + .then(function () { + _hover(gd, 250, 50); + assertHoverLabelContent({ + nums: 'x: 1\ny: 3\nz: 2', + name: 'two' + }); - _hover(gd, 250, 270); - assertHoverLabelContent({ - nums: ['f(1.0, 1.0)=5.56', 'f(1.0, 1.0)=0'], - name: ['', ''] - }); - }) - .then(done, done.fail); + _hover(gd, 250, 270); + assertHoverLabelContent({ + nums: ['x: 1\ny: 1\nz: 5.56', 'x: 1\ny: 1\nz: 0'], + name: ['one', 'two'] + }); + }) + .then(function () { + return Plotly.restyle(gd, 'hovertemplate', 'f(%{x:.1f}, %{y:.1f})=%{z}'); + }) + .then(function () { + _hover(gd, 250, 50); + assertHoverLabelContent({ + nums: 'f(1.0, 3.0)=2', + name: '' + }); + + _hover(gd, 250, 270); + assertHoverLabelContent({ + nums: ['f(1.0, 1.0)=5.56', 'f(1.0, 1.0)=0'], + name: ['', ''] + }); + }) + .then(done, done.fail); }); - it('should display correct label - x/y/z heatmap|contour', function(done) { - Plotly.newPlot(gd, [{ - type: 'heatmap', - x: [1, 1, 2, 2], - y: [1, 2, 1, 2], - z: [1, 2, 3, 4], - customdata: ['a', 'b', 'c', 'd'], - ids: ['A', 'B', 'C', 'D'], - hovertemplate: '%{customdata} | %{id}%{data.type}: %{pointNumber}' - }], { - width: 400, - height: 400, - margin: {l: 0, t: 0, r: 0, b: 0} - }) - .then(function() { - _hover(gd, 50, 50); - assertHoverLabelContent({ - nums: 'b | B', - name: 'heatmap: 1' - }); + it('should display correct label - x/y/z heatmap|contour', function (done) { + Plotly.newPlot( + gd, + [ + { + type: 'heatmap', + x: [1, 1, 2, 2], + y: [1, 2, 1, 2], + z: [1, 2, 3, 4], + customdata: ['a', 'b', 'c', 'd'], + ids: ['A', 'B', 'C', 'D'], + hovertemplate: '%{customdata} | %{id}%{data.type}: %{pointNumber}' + } + ], + { + width: 400, + height: 400, + margin: { l: 0, t: 0, r: 0, b: 0 } + } + ) + .then(function () { + _hover(gd, 50, 50); + assertHoverLabelContent({ + nums: 'b | B', + name: 'heatmap: 1' + }); - _hover(gd, 250, 300); - assertHoverLabelContent({ - nums: 'c | C', - name: 'heatmap: 2' - }); - }) - .then(function() { return Plotly.restyle(gd, 'type', 'contour'); }) - .then(function() { - _hover(gd, 50, 50); - assertHoverLabelContent({ - nums: 'b | B', - name: 'contour: 1' - }); + _hover(gd, 250, 300); + assertHoverLabelContent({ + nums: 'c | C', + name: 'heatmap: 2' + }); + }) + .then(function () { + return Plotly.restyle(gd, 'type', 'contour'); + }) + .then(function () { + _hover(gd, 50, 50); + assertHoverLabelContent({ + nums: 'b | B', + name: 'contour: 1' + }); - _hover(gd, 250, 300); - assertHoverLabelContent({ - nums: 'c | C', - name: 'contour: 2' - }); - }) - .then(done, done.fail); + _hover(gd, 250, 300); + assertHoverLabelContent({ + nums: 'c | C', + name: 'contour: 2' + }); + }) + .then(done, done.fail); }); - it('should display correct label when customdata is typed array - x/y/z heatmap|contour', function(done) { - Plotly.newPlot(gd, [{ - type: 'heatmap', - x: [1, 1, 2, 2], - y: [1, 2, 1, 2], - z: [1, 2, 3, 4], - customdata: new Float64Array([100, 200, 300, 400]), - hovertemplate: '%{customdata}%{data.type}: %{pointNumber}' - }], { - width: 400, - height: 400, - margin: {l: 0, t: 0, r: 0, b: 0} - }) - .then(function() { - _hover(gd, 50, 50); - assertHoverLabelContent({ - nums: '200', - name: 'heatmap: 1' - }); - _hover(gd, 250, 300); - assertHoverLabelContent({ - nums: '300', - name: 'heatmap: 2' - }); - }) - .then(function() { return Plotly.restyle(gd, 'type', 'contour'); }) - .then(function() { - _hover(gd, 50, 50); - assertHoverLabelContent({ - nums: '200', - name: 'contour: 1' - }); - _hover(gd, 250, 300); - assertHoverLabelContent({ - nums: '300', - name: 'contour: 2' - }); - }) - .then(done, done.fail); + it('should display correct label when customdata is typed array - x/y/z heatmap|contour', function (done) { + Plotly.newPlot( + gd, + [ + { + type: 'heatmap', + x: [1, 1, 2, 2], + y: [1, 2, 1, 2], + z: [1, 2, 3, 4], + customdata: new Float64Array([100, 200, 300, 400]), + hovertemplate: '%{customdata}%{data.type}: %{pointNumber}' + } + ], + { + width: 400, + height: 400, + margin: { l: 0, t: 0, r: 0, b: 0 } + } + ) + .then(function () { + _hover(gd, 50, 50); + assertHoverLabelContent({ + nums: '200', + name: 'heatmap: 1' + }); + _hover(gd, 250, 300); + assertHoverLabelContent({ + nums: '300', + name: 'heatmap: 2' + }); + }) + .then(function () { + return Plotly.restyle(gd, 'type', 'contour'); + }) + .then(function () { + _hover(gd, 50, 50); + assertHoverLabelContent({ + nums: '200', + name: 'contour: 1' + }); + _hover(gd, 250, 300); + assertHoverLabelContent({ + nums: '300', + name: 'contour: 2' + }); + }) + .then(done, done.fail); }); - it('should display correct label when customdata contains typed arrays - z heatmap|contour', function(done) { - Plotly.newPlot(gd, [{ - type: 'heatmap', - z: [[1, 3],[2, 4]], - customdata:[new Float64Array([100, 300]), new Float64Array([200, 400])], - hovertemplate: '%{customdata}%{data.type}: %{pointNumber}' - }], { - width: 400, - height: 400, - margin: {l: 0, t: 0, r: 0, b: 0} - }) - .then(function() { - _hover(gd, 50, 50); - assertHoverLabelContent({ - nums: '200', - name: 'heatmap: 1,0' - }); - _hover(gd, 250, 300); - assertHoverLabelContent({ - nums: '300', - name: 'heatmap: 0,1' - }); - }) - .then(function() { return Plotly.restyle(gd, 'type', 'contour'); }) - .then(function() { - _hover(gd, 50, 50); - assertHoverLabelContent({ - nums: '200', - name: 'contour: 1,0' - }); - _hover(gd, 250, 300); - assertHoverLabelContent({ - nums: '300', - name: 'contour: 0,1' - }); - }) - .then(done, done.fail); + it('should display correct label when customdata contains typed arrays - z heatmap|contour', function (done) { + Plotly.newPlot( + gd, + [ + { + type: 'heatmap', + z: [ + [1, 3], + [2, 4] + ], + customdata: [new Float64Array([100, 300]), new Float64Array([200, 400])], + hovertemplate: '%{customdata}%{data.type}: %{pointNumber}' + } + ], + { + width: 400, + height: 400, + margin: { l: 0, t: 0, r: 0, b: 0 } + } + ) + .then(function () { + _hover(gd, 50, 50); + assertHoverLabelContent({ + nums: '200', + name: 'heatmap: 1,0' + }); + _hover(gd, 250, 300); + assertHoverLabelContent({ + nums: '300', + name: 'heatmap: 0,1' + }); + }) + .then(function () { + return Plotly.restyle(gd, 'type', 'contour'); + }) + .then(function () { + _hover(gd, 50, 50); + assertHoverLabelContent({ + nums: '200', + name: 'contour: 1,0' + }); + _hover(gd, 250, 300); + assertHoverLabelContent({ + nums: '300', + name: 'contour: 0,1' + }); + }) + .then(done, done.fail); }); }); - describe('hover info for negative data on a log axis', function() { - it('shows negative data even though it is infinitely off-screen', function(done) { + describe('hover info for negative data on a log axis', function () { + it('shows negative data even though it is infinitely off-screen', function (done) { var gd = createGraphDiv(); - Plotly.newPlot(gd, [{x: [1, 2, 3], y: [1, -5, 10]}], { + Plotly.newPlot(gd, [{ x: [1, 2, 3], y: [1, -5, 10] }], { hovermode: 'x', - yaxis: {type: 'log'}, + yaxis: { type: 'log' }, width: 500, height: 400, - margin: {l: 0, t: 0, r: 0, b: 0} - }) - .then(function() { - _hover(gd, 250, 200); - assertHoverLabelContent({ - nums: '\u22125', // unicode minus - axis: '2' - }); + margin: { l: 0, t: 0, r: 0, b: 0 } }) - .then(done, done.fail); + .then(function () { + _hover(gd, 250, 200); + assertHoverLabelContent({ + nums: '\u22125', // unicode minus + axis: '2' + }); + }) + .then(done, done.fail); }); }); - describe('histogram hover info', function() { - it('shows the data range when bins have multiple values', function(done) { + describe('histogram hover info', function () { + it('shows the data range when bins have multiple values', function (done) { var gd = createGraphDiv(); - Plotly.newPlot(gd, [{ - x: [0, 2, 3, 4, 5, 6, 7], - xbins: {start: -0.5, end: 8.5, size: 3}, - type: 'histogram' - }], { - hovermode: 'x', - width: 500, - height: 400, - margin: {l: 0, t: 0, r: 0, b: 0} - }) - .then(function() { - var pts = null; - gd.once('plotly_hover', function(e) { pts = e.points; }); - - _hoverNatural(gd, 250, 200); - assertHoverLabelContent({nums: '3', axis: '3 - 5'}); - if(pts === null) fail('no hover evt triggered'); - expect(pts.length).toBe(1); - - var pt = pts[0]; - expect(pt.curveNumber).toBe(0); - expect(pt.binNumber).toBe(1); - expect(pt.pointNumbers).toEqual([2, 3, 4]); - expect(pt.x).toBe(4); - expect(pt.y).toBe(3); - expect(pt.data).toBe(gd.data[0]); - expect(pt.fullData).toBe(gd._fullData[0]); - expect(pt.xaxis).toBe(gd._fullLayout.xaxis); - expect(pt.yaxis).toBe(gd._fullLayout.yaxis); - }) - .then(function() { - var pts = null; - gd.once('plotly_hover', function(e) { pts = e.points; }); - - _hoverNatural(gd, 250, 200); - expect(pts).toBe(null, 'should not emit hover event on same pt'); - }) - .then(function() { - var pts = null; - gd.once('plotly_hover', function(e) { pts = e.points; }); - - _hoverNatural(gd, 350, 200); - assertHoverLabelContent({nums: '2', axis: '6 - 8'}); - expect(pts.length).toBe(1); - - var pt = pts[0]; - expect(pt.curveNumber).toBe(0); - expect(pt.binNumber).toBe(2); - expect(pt.pointNumbers).toEqual([5, 6]); - expect(pt.x).toBe(7); - expect(pt.y).toBe(2); - expect(pt.data).toBe(gd.data[0]); - expect(pt.fullData).toBe(gd._fullData[0]); - expect(pt.xaxis).toBe(gd._fullLayout.xaxis); - expect(pt.yaxis).toBe(gd._fullLayout.yaxis); - }) - .then(done, done.fail); + Plotly.newPlot( + gd, + [ + { + x: [0, 2, 3, 4, 5, 6, 7], + xbins: { start: -0.5, end: 8.5, size: 3 }, + type: 'histogram' + } + ], + { + hovermode: 'x', + width: 500, + height: 400, + margin: { l: 0, t: 0, r: 0, b: 0 } + } + ) + .then(function () { + var pts = null; + gd.once('plotly_hover', function (e) { + pts = e.points; + }); + + _hoverNatural(gd, 250, 200); + assertHoverLabelContent({ nums: '3', axis: '3 - 5' }); + if (pts === null) fail('no hover evt triggered'); + expect(pts.length).toBe(1); + + var pt = pts[0]; + expect(pt.curveNumber).toBe(0); + expect(pt.binNumber).toBe(1); + expect(pt.pointNumbers).toEqual([2, 3, 4]); + expect(pt.x).toBe(4); + expect(pt.y).toBe(3); + expect(pt.data).toBe(gd.data[0]); + expect(pt.fullData).toBe(gd._fullData[0]); + expect(pt.xaxis).toBe(gd._fullLayout.xaxis); + expect(pt.yaxis).toBe(gd._fullLayout.yaxis); + }) + .then(function () { + var pts = null; + gd.once('plotly_hover', function (e) { + pts = e.points; + }); + + _hoverNatural(gd, 250, 200); + expect(pts).toBe(null, 'should not emit hover event on same pt'); + }) + .then(function () { + var pts = null; + gd.once('plotly_hover', function (e) { + pts = e.points; + }); + + _hoverNatural(gd, 350, 200); + assertHoverLabelContent({ nums: '2', axis: '6 - 8' }); + expect(pts.length).toBe(1); + + var pt = pts[0]; + expect(pt.curveNumber).toBe(0); + expect(pt.binNumber).toBe(2); + expect(pt.pointNumbers).toEqual([5, 6]); + expect(pt.x).toBe(7); + expect(pt.y).toBe(2); + expect(pt.data).toBe(gd.data[0]); + expect(pt.fullData).toBe(gd._fullData[0]); + expect(pt.xaxis).toBe(gd._fullLayout.xaxis); + expect(pt.yaxis).toBe(gd._fullLayout.yaxis); + }) + .then(done, done.fail); }); - it('shows the exact data when bins have single values', function(done) { + it('shows the exact data when bins have single values', function (done) { var gd = createGraphDiv(); - Plotly.newPlot(gd, [{ - // even though the data aren't regularly spaced, each bin only has - // one data value in it so we see exactly that value - x: [0, 0, 3.3, 3.3, 3.3, 7, 7], - xbins: {start: -0.5, end: 8.5, size: 3}, - type: 'histogram' - }], { - hovermode: 'x', - width: 500, - height: 400, - margin: {l: 0, t: 0, r: 0, b: 0} - }) - .then(function() { - _hover(gd, 250, 200); - assertHoverLabelContent({ - nums: '3', - axis: '3.3' - }); - }) - .then(function() { return Plotly.restyle(gd, 'hovertext', 'LOOK'); }) - .then(function() { - _hover(gd, 250, 200); - assertHoverLabelContent({ - nums: '3\nLOOK', - axis: '3.3' - }); - }) - .then(done, done.fail); - }); + Plotly.newPlot( + gd, + [ + { + // even though the data aren't regularly spaced, each bin only has + // one data value in it so we see exactly that value + x: [0, 0, 3.3, 3.3, 3.3, 7, 7], + xbins: { start: -0.5, end: 8.5, size: 3 }, + type: 'histogram' + } + ], + { + hovermode: 'x', + width: 500, + height: 400, + margin: { l: 0, t: 0, r: 0, b: 0 } + } + ) + .then(function () { + _hover(gd, 250, 200); + assertHoverLabelContent({ + nums: '3', + axis: '3.3' + }); + }) + .then(function () { + return Plotly.restyle(gd, 'hovertext', 'LOOK'); + }) + .then(function () { + _hover(gd, 250, 200); + assertHoverLabelContent({ + nums: '3\nLOOK', + axis: '3.3' + }); + }) + .then(done, done.fail); + }); - it('will show a category range if you ask nicely', function(done) { + it('will show a category range if you ask nicely', function (done) { var gd = createGraphDiv(); - Plotly.newPlot(gd, [{ - // even though the data aren't regularly spaced, each bin only has - // one data value in it so we see exactly that value - x: [ - 'bread', 'cheese', 'artichokes', 'soup', 'beans', 'nuts', - 'pizza', 'potatoes', 'burgers', 'beans', 'beans', 'beans' + Plotly.newPlot( + gd, + [ + { + // even though the data aren't regularly spaced, each bin only has + // one data value in it so we see exactly that value + x: [ + 'bread', + 'cheese', + 'artichokes', + 'soup', + 'beans', + 'nuts', + 'pizza', + 'potatoes', + 'burgers', + 'beans', + 'beans', + 'beans' + ], + xbins: { start: -0.5, end: 8.5, size: 3 }, + type: 'histogram' + } ], - xbins: {start: -0.5, end: 8.5, size: 3}, - type: 'histogram' - }], { - hovermode: 'x', - width: 500, - height: 400, - margin: {l: 0, t: 0, r: 0, b: 0} - }) - .then(function() { - _hover(gd, 250, 200); - assertHoverLabelContent({ - nums: '6', - axis: 'soup - nuts' - }); - }) - .then(done, done.fail); + { + hovermode: 'x', + width: 500, + height: 400, + margin: { l: 0, t: 0, r: 0, b: 0 } + } + ) + .then(function () { + _hover(gd, 250, 200); + assertHoverLabelContent({ + nums: '6', + axis: 'soup - nuts' + }); + }) + .then(done, done.fail); }); - it('will update when switching from one empty bin to another', done => { + it('will update when switching from one empty bin to another', (done) => { const gd = createGraphDiv(); - Plotly - .newPlot( - gd, - [{ + Plotly.newPlot( + gd, + [ + { x: [ - 0.025,0.025,0.025,0.025,0.025, - 0.075,0.075,0.075,0.075,0.075, - 0.125,0.125,0.125,0.125,0.125,0.125, - 0.175,0.175,0.175,0.175, - 0.475,0.475,0.475 + 0.025, 0.025, 0.025, 0.025, 0.025, 0.075, 0.075, 0.075, 0.075, 0.075, 0.125, 0.125, 0.125, + 0.125, 0.125, 0.125, 0.175, 0.175, 0.175, 0.175, 0.475, 0.475, 0.475 ], - xbins: { start: 0, end: 0.5, size: 0.10 }, + xbins: { start: 0, end: 0.5, size: 0.1 }, type: 'histogram' - }], - { - hovermode: 'x', - width: 500, - height: 400, - margin: {l: 0, t: 0, r: 0, b: 0} } - ) + ], + { + hovermode: 'x', + width: 500, + height: 400, + margin: { l: 0, t: 0, r: 0, b: 0 } + } + ) .then(() => { let hoverData; - gd.on('plotly_hover', e => { hoverData = e; }); + gd.on('plotly_hover', (e) => { + hoverData = e; + }); _hoverNatural(gd, 250, 200); - expect(hoverData.points[0].binNumber).toBe(2) + expect(hoverData.points[0].binNumber).toBe(2); _hoverNatural(gd, 300, 200); - expect(hoverData.points[0].binNumber).toBe(3) + expect(hoverData.points[0].binNumber).toBe(3); }) .then(done, done.fail); }); }); - ['candlestick', 'ohlc'].forEach(function(type) { - describe(type + ' hoverinfo', function() { + ['candlestick', 'ohlc'].forEach(function (type) { + describe(type + ' hoverinfo', function () { var gd; function financeMock(traceUpdates, layoutUpdates) { return { - data: [Lib.extendFlat({}, { - type: type, - x: ['2011-01-01', '2011-01-02', '2011-01-03'], - open: [1, 2, 3], - high: [3, 4, 5], - low: [0, 1, 2], - close: [0, 3, 2] - }, traceUpdates || {})], - layout: Lib.extendDeep({}, { - hovermode: 'x', - width: 400, - height: 400, - margin: {l: 50, r: 50, t: 50, b: 50} - }, layoutUpdates || {}) + data: [ + Lib.extendFlat( + {}, + { + type: type, + x: ['2011-01-01', '2011-01-02', '2011-01-03'], + open: [1, 2, 3], + high: [3, 4, 5], + low: [0, 1, 2], + close: [0, 3, 2] + }, + traceUpdates || {} + ) + ], + layout: Lib.extendDeep( + {}, + { + hovermode: 'x', + width: 400, + height: 400, + margin: { l: 50, r: 50, t: 50, b: 50 } + }, + layoutUpdates || {} + ) }; } - beforeEach(function() { + beforeEach(function () { gd = createGraphDiv(); }); - it('has the right basic and event behavior', function(done) { + it('has the right basic and event behavior', function (done) { var pts; - Plotly.newPlot(gd, financeMock({ - customdata: [11, 22, 33] - })) - .then(function() { - gd.on('plotly_hover', function(e) { pts = e.points; }); - - _hoverNatural(gd, 150, 150); - assertHoverLabelContent({ - nums: 'open: 2\nhigh: 4\nlow: 1\nclose: 3 ▲', - axis: 'Jan 2, 2011' - }); - }) - .then(function() { - expect(pts).toBeDefined(); - expect(pts.length).toBe(1); - expect(pts[0]).toEqual(jasmine.objectContaining({ - x: '2011-01-02', - open: 2, - high: 4, - low: 1, - close: 3, - customdata: 22, - curveNumber: 0, - pointNumber: 1, - data: gd.data[0], - fullData: gd._fullData[0], - xaxis: gd._fullLayout.xaxis, - yaxis: gd._fullLayout.yaxis - })); - - return Plotly.relayout(gd, {hovermode: 'closest'}); - }) - .then(function() { - _hoverNatural(gd, 150, 150); - assertHoverLabelContent({ - nums: 'Jan 2, 2011\nopen: 2\nhigh: 4\nlow: 1\nclose: 3 ▲' - }); - }) - .then(done, done.fail); + Plotly.newPlot( + gd, + financeMock({ + customdata: [11, 22, 33] + }) + ) + .then(function () { + gd.on('plotly_hover', function (e) { + pts = e.points; + }); + + _hoverNatural(gd, 150, 150); + assertHoverLabelContent({ + nums: 'open: 2\nhigh: 4\nlow: 1\nclose: 3 ▲', + axis: 'Jan 2, 2011' + }); + }) + .then(function () { + expect(pts).toBeDefined(); + expect(pts.length).toBe(1); + expect(pts[0]).toEqual( + jasmine.objectContaining({ + x: '2011-01-02', + open: 2, + high: 4, + low: 1, + close: 3, + customdata: 22, + curveNumber: 0, + pointNumber: 1, + data: gd.data[0], + fullData: gd._fullData[0], + xaxis: gd._fullLayout.xaxis, + yaxis: gd._fullLayout.yaxis + }) + ); + + return Plotly.relayout(gd, { hovermode: 'closest' }); + }) + .then(function () { + _hoverNatural(gd, 150, 150); + assertHoverLabelContent({ + nums: 'Jan 2, 2011\nopen: 2\nhigh: 4\nlow: 1\nclose: 3 ▲' + }); + }) + .then(done, done.fail); }); - it('shows correct labels in split mode', function(done) { + it('shows correct labels in split mode', function (done) { var pts; - Plotly.newPlot(gd, financeMock({ - customdata: [11, 22, 33], - hoverlabel: { - split: true - } - })) - .then(function() { - gd.on('plotly_hover', function(e) { pts = e.points; }); - - _hoverNatural(gd, 150, 150); - assertHoverLabelContent({ - nums: ['high: 4', 'open: 2', 'close: 3', 'low: 1'], - name: ['', '', '', ''], - axis: 'Jan 2, 2011' - }); - }) - .then(function() { - expect(pts).toBeDefined(); - expect(pts.length).toBe(4); - expect(pts[0]).toEqual(jasmine.objectContaining({ - x: '2011-01-02', - high: 4, - customdata: 22, - })); - expect(pts[1]).toEqual(jasmine.objectContaining({ - x: '2011-01-02', - open: 2, - customdata: 22, - })); - expect(pts[2]).toEqual(jasmine.objectContaining({ - x: '2011-01-02', - close: 3, - customdata: 22, - })); - expect(pts[3]).toEqual(jasmine.objectContaining({ - x: '2011-01-02', - low: 1, - customdata: 22, - })); - }) - .then(function() { - _hoverNatural(gd, 200, 150); - assertHoverLabelContent({ - nums: ['high: 5', 'open: 3', 'close: 2\nlow: 2'], - name: ['', '', ''], - axis: 'Jan 3, 2011' - }); - }) - .then(done, done.fail); + Plotly.newPlot( + gd, + financeMock({ + customdata: [11, 22, 33], + hoverlabel: { + split: true + } + }) + ) + .then(function () { + gd.on('plotly_hover', function (e) { + pts = e.points; + }); + + _hoverNatural(gd, 150, 150); + assertHoverLabelContent({ + nums: ['high: 4', 'open: 2', 'close: 3', 'low: 1'], + name: ['', '', '', ''], + axis: 'Jan 2, 2011' + }); + }) + .then(function () { + expect(pts).toBeDefined(); + expect(pts.length).toBe(4); + expect(pts[0]).toEqual( + jasmine.objectContaining({ + x: '2011-01-02', + high: 4, + customdata: 22 + }) + ); + expect(pts[1]).toEqual( + jasmine.objectContaining({ + x: '2011-01-02', + open: 2, + customdata: 22 + }) + ); + expect(pts[2]).toEqual( + jasmine.objectContaining({ + x: '2011-01-02', + close: 3, + customdata: 22 + }) + ); + expect(pts[3]).toEqual( + jasmine.objectContaining({ + x: '2011-01-02', + low: 1, + customdata: 22 + }) + ); + }) + .then(function () { + _hoverNatural(gd, 200, 150); + assertHoverLabelContent({ + nums: ['high: 5', 'open: 3', 'close: 2\nlow: 2'], + name: ['', '', ''], + axis: 'Jan 3, 2011' + }); + }) + .then(done, done.fail); }); - it('shows text if text is in hoverinfo', function(done) { - Plotly.newPlot(gd, financeMock({text: ['A', 'B', 'C']})) - .then(function() { - _hover(gd, 150, 150); - assertHoverLabelContent({ - nums: 'open: 2\nhigh: 4\nlow: 1\nclose: 3 ▲\nB', - axis: 'Jan 2, 2011' - }); - - return Plotly.restyle(gd, {hoverinfo: 'x+text'}); - }) - .then(function() { - _hover(gd, 150, 150); - assertHoverLabelContent({ - nums: 'B', - axis: 'Jan 2, 2011' - }); + it('shows text if text is in hoverinfo', function (done) { + Plotly.newPlot(gd, financeMock({ text: ['A', 'B', 'C'] })) + .then(function () { + _hover(gd, 150, 150); + assertHoverLabelContent({ + nums: 'open: 2\nhigh: 4\nlow: 1\nclose: 3 ▲\nB', + axis: 'Jan 2, 2011' + }); - return Plotly.restyle(gd, {hoverinfo: 'x+y'}); - }) - .then(function() { - _hover(gd, 150, 150); - assertHoverLabelContent({ - nums: 'open: 2\nhigh: 4\nlow: 1\nclose: 3 ▲', - axis: 'Jan 2, 2011' - }); - }) - .then(done, done.fail); + return Plotly.restyle(gd, { hoverinfo: 'x+text' }); + }) + .then(function () { + _hover(gd, 150, 150); + assertHoverLabelContent({ + nums: 'B', + axis: 'Jan 2, 2011' + }); + + return Plotly.restyle(gd, { hoverinfo: 'x+y' }); + }) + .then(function () { + _hover(gd, 150, 150); + assertHoverLabelContent({ + nums: 'open: 2\nhigh: 4\nlow: 1\nclose: 3 ▲', + axis: 'Jan 2, 2011' + }); + }) + .then(done, done.fail); }); }); }); - describe('hoverformat', function() { - var data = [{ - x: [1, 2, 3], - y: [0.12345, 0.23456, 0.34567] - }]; + describe('hoverformat', function () { + var data = [ + { + x: [1, 2, 3], + y: [0.12345, 0.23456, 0.34567] + } + ]; var layout = { hovermode: 'x', yaxis: { showticklabels: true, hoverformat: ',.2r' }, @@ -1499,145 +1673,151 @@ describe('hover info', function() { height: 400 }; - beforeEach(function() { + beforeEach(function () { this.gd = createGraphDiv(); }); - it('should display the correct format when ticklabels true', function(done) { + it('should display the correct format when ticklabels true', function (done) { Plotly.newPlot(this.gd, data, layout) - .then(function() { - mouseEvent('mousemove', 303, 213); + .then(function () { + mouseEvent('mousemove', 303, 213); - assertHoverLabelContent({ - nums: '0.23', - axis: '2' - }); - }) - .then(done, done.fail); + assertHoverLabelContent({ + nums: '0.23', + axis: '2' + }); + }) + .then(done, done.fail); }); - it('should display the correct format when ticklabels false', function(done) { + it('should display the correct format when ticklabels false', function (done) { layout.yaxis.showticklabels = false; Plotly.newPlot(this.gd, data, layout) - .then(function() { - mouseEvent('mousemove', 303, 213); + .then(function () { + mouseEvent('mousemove', 303, 213); - assertHoverLabelContent({ - nums: '0.23', - axis: '2' - }); - }) - .then(done, done.fail); + assertHoverLabelContent({ + nums: '0.23', + axis: '2' + }); + }) + .then(done, done.fail); }); }); - describe('textmode', function() { - var data = [{ - x: [1, 2, 3, 4], - y: [2, 3, 4, 5], - mode: 'text', - hoverinfo: 'text', - text: ['test', null, 42, undefined] - }]; + describe('textmode', function () { + var data = [ + { + x: [1, 2, 3, 4], + y: [2, 3, 4, 5], + mode: 'text', + hoverinfo: 'text', + text: ['test', null, 42, undefined] + } + ]; var layout = { width: 600, height: 400 }; - beforeEach(function(done) { + beforeEach(function (done) { Plotly.newPlot(createGraphDiv(), data, layout).then(done); }); - it('should show text labels', function() { + it('should show text labels', function () { mouseEvent('mousemove', 108, 303); assertHoverLabelContent({ nums: 'test' }); }); - it('should show number labels', function() { + it('should show number labels', function () { mouseEvent('mousemove', 363, 173); assertHoverLabelContent({ nums: '42' }); }); - it('should not show null text labels', function() { + it('should not show null text labels', function () { mouseEvent('mousemove', 229, 239); assertHoverLabelContent({}); }); - it('should not show undefined text labels', function() { + it('should not show undefined text labels', function () { mouseEvent('mousemove', 493, 108); assertHoverLabelContent({}); }); }); - describe('hover events', function() { - var data = [{x: [1, 2, 3], y: [1, 3, 2], type: 'bar'}]; - var layout = {width: 600, height: 400}; + describe('hover events', function () { + var data = [{ x: [1, 2, 3], y: [1, 3, 2], type: 'bar' }]; + var layout = { width: 600, height: 400 }; var gd; - beforeEach(function(done) { + beforeEach(function (done) { gd = createGraphDiv(); Plotly.newPlot(gd, data, layout).then(done); }); - it('should skip the hover event if explicitly instructed', function(done) { + it('should skip the hover event if explicitly instructed', function (done) { var hoverHandler = jasmine.createSpy(); gd.on('plotly_hover', hoverHandler); var gdBB = gd.getBoundingClientRect(); - var event = {clientX: gdBB.left + 300, clientY: gdBB.top + 200}; + var event = { clientX: gdBB.left + 300, clientY: gdBB.top + 200 }; - Promise.resolve().then(function() { - Fx.hover(gd, event, 'xy', true); - }) - .then(function() { - expect(hoverHandler).not.toHaveBeenCalled(); - }) - .then(done, done.fail); + Promise.resolve() + .then(function () { + Fx.hover(gd, event, 'xy', true); + }) + .then(function () { + expect(hoverHandler).not.toHaveBeenCalled(); + }) + .then(done, done.fail); }); - it('should emit events only if the event looks user-driven', function(done) { + it('should emit events only if the event looks user-driven', function (done) { var hoverHandler = jasmine.createSpy(); gd.on('plotly_hover', hoverHandler); var gdBB = gd.getBoundingClientRect(); - var event = {clientX: gdBB.left + 300, clientY: gdBB.top + 200}; + var event = { clientX: gdBB.left + 300, clientY: gdBB.top + 200 }; - Promise.resolve().then(function() { - Fx.hover(gd, event, 'xy'); - }) - .then(delay(HOVERMINTIME * 1.1)) - .then(function() { - Fx.unhover(gd); - }) - .then(function() { - expect(hoverHandler).not.toHaveBeenCalled(); - var dragger = gd.querySelector('.nsewdrag'); + Promise.resolve() + .then(function () { + Fx.hover(gd, event, 'xy'); + }) + .then(delay(HOVERMINTIME * 1.1)) + .then(function () { + Fx.unhover(gd); + }) + .then(function () { + expect(hoverHandler).not.toHaveBeenCalled(); + var dragger = gd.querySelector('.nsewdrag'); - Fx.hover(gd, Lib.extendFlat({target: dragger}, event), 'xy'); - }) - .then(function() { - expect(hoverHandler).toHaveBeenCalledTimes(1); - }) - .then(done, done.fail); + Fx.hover(gd, Lib.extendFlat({ target: dragger }, event), 'xy'); + }) + .then(function () { + expect(hoverHandler).toHaveBeenCalledTimes(1); + }) + .then(done, done.fail); }); }); - describe('overflowing hover labels', function() { - var trace = {y: [1, 2, 3], text: ['', 'a
b
c', '']}; + describe('overflowing hover labels', function () { + var trace = { y: [1, 2, 3], text: ['', 'a
b
c', ''] }; var data = [trace, trace, trace, trace, trace, trace, trace, trace, trace, trace]; var layout = { - width: 600, height: 600, showlegend: false, - margin: {l: 100, r: 100, t: 100, b: 100}, + width: 600, + height: 600, + showlegend: false, + margin: { l: 100, r: 100, t: 100, b: 100 }, hovermode: 'x' }; var gd; - beforeEach(function(done) { + beforeEach(function (done) { gd = createGraphDiv(); Plotly.newPlot(gd, data, layout).then(done); }); @@ -1646,34 +1826,36 @@ describe('hover info', function() { return d3Select(gd).selectAll('g.hovertext').size(); } - it('shows as many labels as will fit on the div, not on the subplot, when labels do not overlap the axis label', function(done) { + it('shows as many labels as will fit on the div, not on the subplot, when labels do not overlap the axis label', function (done) { _hoverNatural(gd, 200, 200); expect(labelCount()).toBe(8); - Plotly.relayout(gd, {'yaxis.domain': [0.48, 0.52]}) - .then(function() { - _hoverNatural(gd, 150, 200); - _hoverNatural(gd, 200, 200); + Plotly.relayout(gd, { 'yaxis.domain': [0.48, 0.52] }) + .then(function () { + _hoverNatural(gd, 150, 200); + _hoverNatural(gd, 200, 200); - expect(labelCount()).toBe(8); - }) - .then(done, done.fail); + expect(labelCount()).toBe(8); + }) + .then(done, done.fail); }); }); - describe('overlapping hover labels', function() { - var trace = {y: [1, 2, 3], x: ['01.01.2020', '02.01.2020', '03.01.2020'], text: ['', 'a
b
c', '']}; + describe('overlapping hover labels', function () { + var trace = { y: [1, 2, 3], x: ['01.01.2020', '02.01.2020', '03.01.2020'], text: ['', 'a
b
c', ''] }; var data = [trace, trace, trace, trace, trace, trace, trace, trace, trace, trace]; var layout = { - width: 600, height: 600, showlegend: false, - margin: {l: 100, r: 100, t: 100, b: 100}, + width: 600, + height: 600, + showlegend: false, + margin: { l: 100, r: 100, t: 100, b: 100 }, hovermode: 'x' }; var gd; - beforeEach(function(done) { + beforeEach(function (done) { gd = createGraphDiv(); Plotly.newPlot(gd, data, layout).then(done); }); @@ -1682,32 +1864,36 @@ describe('hover info', function() { return d3Select(gd).selectAll('g.hovertext').size(); } - it('does not show labels that would overlap the axis hover label', function(done) { + it('does not show labels that would overlap the axis hover label', function (done) { _hoverNatural(gd, 200, 200); expect(labelCount()).toBe(6); - Plotly.relayout(gd, {'yaxis.domain': [0.48, 0.52]}) - .then(function() { - _hoverNatural(gd, 150, 200); - _hoverNatural(gd, 200, 200); + Plotly.relayout(gd, { 'yaxis.domain': [0.48, 0.52] }) + .then(function () { + _hoverNatural(gd, 150, 200); + _hoverNatural(gd, 200, 200); - expect(labelCount()).toBe(4); - }) - .then(done, done.fail); + expect(labelCount()).toBe(4); + }) + .then(done, done.fail); }); }); - describe('overlapping hover labels of different lengths', function() { - var data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20].map(function(v) {return {x: [100, 200, 300], y: [v, v + 1, v + 2]};}); + describe('overlapping hover labels of different lengths', function () { + var data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20].map(function (v) { + return { x: [100, 200, 300], y: [v, v + 1, v + 2] }; + }); var layout = { - width: 500, height: 400, showlegend: false, - margin: {l: 100, r: 100, t: 100, b: 100}, + width: 500, + height: 400, + showlegend: false, + margin: { l: 100, r: 100, t: 100, b: 100 }, hovermode: 'x' }; var gd; - beforeEach(function(done) { + beforeEach(function (done) { gd = createGraphDiv(); Plotly.newPlot(gd, data, layout).then(done); }); @@ -1716,29 +1902,33 @@ describe('hover info', function() { return d3Select(gd).selectAll('g.hovertext').size(); } - it('does not show labels that would overlap the axis hover label', function(done) { + it('does not show labels that would overlap the axis hover label', function (done) { _hoverNatural(gd, 130, 100); expect(labelCount()).toBe(14); - Plotly.relayout(gd, {'yaxis.domain': [0.2, 0.8]}) - .then(function() { - _hoverNatural(gd, 130, 100); + Plotly.relayout(gd, { 'yaxis.domain': [0.2, 0.8] }) + .then(function () { + _hoverNatural(gd, 130, 100); - expect(labelCount()).toBe(12); - }) - .then(done, done.fail); + expect(labelCount()).toBe(12); + }) + .then(done, done.fail); }); }); - describe('alignment while avoiding overlaps:', function() { + describe('alignment while avoiding overlaps:', function () { var gd; - beforeEach(function() { gd = createGraphDiv(); }); + beforeEach(function () { + gd = createGraphDiv(); + }); function hoverInfoNodes(traceName) { - var g = d3SelectAll('g.hoverlayer g.hovertext').filter(function() { - return !d3Select(this).select('[data-unformatted="' + traceName + '"]').empty(); + var g = d3SelectAll('g.hoverlayer g.hovertext').filter(function () { + return !d3Select(this) + .select('[data-unformatted="' + traceName + '"]') + .empty(); }); return { @@ -1758,19 +1948,23 @@ describe('hover info', function() { function assertLabelsInsideBoxes(nodes, msgPrefix) { var msgPrefixFmt = msgPrefix ? '[' + msgPrefix + '] ' : ''; - assertElemInside(nodes.primaryText, nodes.primaryBox, - msgPrefixFmt + 'Primary text inside box'); - assertElemInside(nodes.secondaryText, nodes.secondaryBox, - msgPrefixFmt + 'Secondary text inside box'); + assertElemInside(nodes.primaryText, nodes.primaryBox, msgPrefixFmt + 'Primary text inside box'); + assertElemInside(nodes.secondaryText, nodes.secondaryBox, msgPrefixFmt + 'Secondary text inside box'); } function assertSecondaryRightToPrimaryBox(nodes, msgPrefix) { var msgPrefixFmt = msgPrefix ? '[' + msgPrefix + '] ' : ''; - assertElemRightTo(nodes.secondaryBox, nodes.primaryBox, - msgPrefixFmt + 'Secondary box right to primary box'); - assertElemTopsAligned(nodes.secondaryBox, nodes.primaryBox, - msgPrefixFmt + 'Top edges of primary and secondary boxes aligned'); + assertElemRightTo( + nodes.secondaryBox, + nodes.primaryBox, + msgPrefixFmt + 'Secondary box right to primary box' + ); + assertElemTopsAligned( + nodes.secondaryBox, + nodes.primaryBox, + msgPrefixFmt + 'Top edges of primary and secondary boxes aligned' + ); } function calcLineOverlap(minA, maxA, minB, maxB) { @@ -1785,7 +1979,7 @@ describe('hover info', function() { return d3Select(gd).selectAll('g.hovertext').size(); } - it('centered-aligned, should render labels inside boxes', function(done) { + it('centered-aligned, should render labels inside boxes', function (done) { var trace1 = { x: ['giraffes'], y: [5], @@ -1801,19 +1995,21 @@ describe('hover info', function() { text: ['San Francisco'] }; var data = [trace1, trace2]; - var layout = {width: 600, height: 300, barmode: 'stack', hovermode: 'x'}; + var layout = { width: 600, height: 300, barmode: 'stack', hovermode: 'x' }; Plotly.newPlot(gd, data, layout) - .then(function() { _hover(gd, 300, 150); }) - .then(function() { - var nodes = ensureCentered(hoverInfoNodes('LA Zoo')); - assertLabelsInsideBoxes(nodes); - assertSecondaryRightToPrimaryBox(nodes); - }) - .then(done, done.fail); + .then(function () { + _hover(gd, 300, 150); + }) + .then(function () { + var nodes = ensureCentered(hoverInfoNodes('LA Zoo')); + assertLabelsInsideBoxes(nodes); + assertSecondaryRightToPrimaryBox(nodes); + }) + .then(done, done.fail); }); - it('centered-aligned, should stack nicely upon each other', function(done) { + it('centered-aligned, should stack nicely upon each other', function (done) { var trace1 = { x: ['giraffes'], y: [5], @@ -1836,82 +2032,94 @@ describe('hover info', function() { text: ['New York'] }; var data = [trace1, trace2, trace3]; - var layout = {width: 600, height: 300, hovermode: 'x'}; + var layout = { width: 600, height: 300, hovermode: 'x' }; Plotly.newPlot(gd, data, layout) - .then(function() { _hover(gd, 300, 150); }) - .then(function() { - var nodesLA = ensureCentered(hoverInfoNodes('LA Zoo')); - var nodesSF = ensureCentered(hoverInfoNodes('SF Zoo')); - - // Ensure layout correct - assertLabelsInsideBoxes(nodesLA, 'LA Zoo'); - assertLabelsInsideBoxes(nodesSF, 'SF Zoo'); - assertSecondaryRightToPrimaryBox(nodesLA, 'LA Zoo'); - assertSecondaryRightToPrimaryBox(nodesSF, 'SF Zoo'); - - // Ensure stacking, finally - var boxLABB = nodesLA.primaryBox.getBoundingClientRect(); - var boxSFBB = nodesSF.primaryBox.getBoundingClientRect(); - - // Be robust against floating point arithmetic and subtle future layout changes - expect(calcLineOverlap(boxLABB.top, boxLABB.bottom, boxSFBB.top, boxSFBB.bottom)) - .toBeWithin(0, 1); - }) - .then(done, done.fail); + .then(function () { + _hover(gd, 300, 150); + }) + .then(function () { + var nodesLA = ensureCentered(hoverInfoNodes('LA Zoo')); + var nodesSF = ensureCentered(hoverInfoNodes('SF Zoo')); + + // Ensure layout correct + assertLabelsInsideBoxes(nodesLA, 'LA Zoo'); + assertLabelsInsideBoxes(nodesSF, 'SF Zoo'); + assertSecondaryRightToPrimaryBox(nodesLA, 'LA Zoo'); + assertSecondaryRightToPrimaryBox(nodesSF, 'SF Zoo'); + + // Ensure stacking, finally + var boxLABB = nodesLA.primaryBox.getBoundingClientRect(); + var boxSFBB = nodesSF.primaryBox.getBoundingClientRect(); + + // Be robust against floating point arithmetic and subtle future layout changes + expect(calcLineOverlap(boxLABB.top, boxLABB.bottom, boxSFBB.top, boxSFBB.bottom)).toBeWithin(0, 1); + }) + .then(done, done.fail); }); - it('should stack multi-line trace-name labels nicely', function(done) { + it('should stack multi-line trace-name labels nicely', function (done) { var name = 'Multi
line
trace
name'; var name2 = 'Multi
line
trace
name2'; - Plotly.newPlot(gd, [{ - y: [1, 2, 1], - name: name, - hoverlabel: {namelength: -1}, - hoverinfo: 'x+y+name' - }, { - y: [1, 2, 1], - name: name2, - hoverinfo: 'x+y+name', - hoverlabel: {namelength: -1} - }], { - hovermode: 'x', - width: 600, - height: 300 - }) - .then(function() { _hoverNatural(gd, 209, 12); }) - .then(function() { - var nodes = hoverInfoNodes(name); - var nodes2 = hoverInfoNodes(name2); - - assertLabelsInsideBoxes(nodes, 'trace 0'); - assertLabelsInsideBoxes(nodes2, 'trace 2'); - assertSecondaryRightToPrimaryBox(nodes, 'trace 0'); - assertSecondaryRightToPrimaryBox(nodes2, 'trace 2'); - - var primaryBB = nodes.primaryBox.getBoundingClientRect(); - var primaryBB2 = nodes2.primaryBox.getBoundingClientRect(); - expect(calcLineOverlap(primaryBB.top, primaryBB.bottom, primaryBB2.top, primaryBB2.bottom)) - .toBeWithin(0, 1); - - // there's a bit of a gap in the secondary as they do have - // a border (for now) - var secondaryBB = nodes.secondaryBox.getBoundingClientRect(); - var secondaryBB2 = nodes2.secondaryBox.getBoundingClientRect(); - expect(calcLineOverlap(secondaryBB.top, secondaryBB.bottom, secondaryBB2.top, secondaryBB2.bottom)) - .toBeWithin(2, 1); - }) - .then(done, done.fail); + Plotly.newPlot( + gd, + [ + { + y: [1, 2, 1], + name: name, + hoverlabel: { namelength: -1 }, + hoverinfo: 'x+y+name' + }, + { + y: [1, 2, 1], + name: name2, + hoverinfo: 'x+y+name', + hoverlabel: { namelength: -1 } + } + ], + { + hovermode: 'x', + width: 600, + height: 300 + } + ) + .then(function () { + _hoverNatural(gd, 209, 12); + }) + .then(function () { + var nodes = hoverInfoNodes(name); + var nodes2 = hoverInfoNodes(name2); + + assertLabelsInsideBoxes(nodes, 'trace 0'); + assertLabelsInsideBoxes(nodes2, 'trace 2'); + assertSecondaryRightToPrimaryBox(nodes, 'trace 0'); + assertSecondaryRightToPrimaryBox(nodes2, 'trace 2'); + + var primaryBB = nodes.primaryBox.getBoundingClientRect(); + var primaryBB2 = nodes2.primaryBox.getBoundingClientRect(); + expect( + calcLineOverlap(primaryBB.top, primaryBB.bottom, primaryBB2.top, primaryBB2.bottom) + ).toBeWithin(0, 1); + + // there's a bit of a gap in the secondary as they do have + // a border (for now) + var secondaryBB = nodes.secondaryBox.getBoundingClientRect(); + var secondaryBB2 = nodes2.secondaryBox.getBoundingClientRect(); + expect( + calcLineOverlap(secondaryBB.top, secondaryBB.bottom, secondaryBB2.top, secondaryBB2.bottom) + ).toBeWithin(2, 1); + }) + .then(done, done.fail); }); - it('does not overlap lebels for different trace types', function(done) { + it('does not overlap lebels for different trace types', function (done) { function trace(name, type, delta) { return { name: name, type: type, y: [0 + delta, 1 + delta, 2 + delta], - x: ['CAT 1', 'CAT 2', 'CAT 3'], + x: ['CAT 1', 'CAT 2', 'CAT 3'] }; } @@ -1919,59 +2127,57 @@ describe('hover info', function() { var barName = 'bar_'; var data = []; var i; - for(i = 0; i < 3; i++) { + for (i = 0; i < 3; i++) { data.push(trace(barName + i, 'bar', 0.0)); data.push(trace(scatterName + i, 'scatter', 0.1)); } var layout = { width: 600, height: 400, - hovermode: 'x', + hovermode: 'x' }; Plotly.newPlot(gd, data, layout) - .then(function() { + .then(function () { _hoverNatural(gd, 200, 200); }) - .then(function() { + .then(function () { expect(labelCount()).toBe(6); }) - .then(function() { + .then(function () { var nodes = []; - for(i = 0; i < 3; i++) { + for (i = 0; i < 3; i++) { nodes.push(hoverInfoNodes(barName + i).secondaryBox.getBoundingClientRect()); nodes.push(hoverInfoNodes(scatterName + i).secondaryBox.getBoundingClientRect()); } - nodes.sort(function(a, b) { return a.top - b.top; }); + nodes.sort(function (a, b) { + return a.top - b.top; + }); - for(i = 0; i < 5; i++) { + for (i = 0; i < 5; i++) { expect( - calcLineOverlap( - nodes[i].top, - nodes[i].bottom, - nodes[i + 1].top, - nodes[i + 1].bottom - ) - ).toBeWithin(2, 1); + calcLineOverlap(nodes[i].top, nodes[i].bottom, nodes[i + 1].top, nodes[i + 1].bottom) + ).toBeWithin(2, 1); } }) - .then(done, done.fail); + .then(done, done.fail); }); }); - describe('constraints info graph viewport', function() { + describe('constraints info graph viewport', function () { var gd; - beforeEach(function() { gd = createGraphDiv(); }); + beforeEach(function () { + gd = createGraphDiv(); + }); - it('hovermode:x common label should fit in the graph div width', function(done) { + it('hovermode:x common label should fit in the graph div width', function (done) { function _assert(msg, exp) { - return function() { + return function () { var label = d3Select('g.axistext'); - if(label.node()) { + if (label.node()) { expect(label.text()).toBe(exp.txt, 'common label text| ' + msg); - expect(Drawing.getTranslate(label).x) - .toBeWithin(exp.lx, 5, 'common label translate-x| ' + msg); + expect(Drawing.getTranslate(label).x).toBeWithin(exp.lx, 5, 'common label translate-x| ' + msg); var startOfPath = label.select('path').attr('d').split('L')[0]; expect(startOfPath).not.toBe('M0,0', 'offset start of label path| ' + msg); @@ -1981,38 +2187,50 @@ describe('hover info', function() { }; } - function _hoverLeft() { return _hover(gd, 30, 300); } + function _hoverLeft() { + return _hover(gd, 30, 300); + } - function _hoverRight() { return _hover(gd, 370, 300); } + function _hoverRight() { + return _hover(gd, 370, 300); + } - Plotly.newPlot(gd, [{ - type: 'bar', - x: ['2019-01-01', '2019-06-01', '2020-01-01'], - y: [2, 5, 10] - }], { - hovermode: 'x', - xaxis: {range: ['2019-02-06', '2019-12-01']}, - margin: {l: 0, r: 0}, - width: 400, - height: 400 - }) - .then(_hoverLeft) - .then(_assert('left-edge hover', {txt: 'Jan 1, 2019', lx: 37})) - .then(_hoverRight) - .then(_assert('right-edge hover', {txt: 'Jan 1, 2020', lx: 362})) - .then(function() { return Plotly.relayout(gd, 'xaxis.side', 'top'); }) - .then(_hoverLeft) - .then(_assert('left-edge hover (side:top)', {txt: 'Jan 1, 2019', lx: 37})) - .then(_hoverRight) - .then(_assert('right-edge hover (side:top)', {txt: 'Jan 1, 2020', lx: 362})) - .then(done, done.fail); + Plotly.newPlot( + gd, + [ + { + type: 'bar', + x: ['2019-01-01', '2019-06-01', '2020-01-01'], + y: [2, 5, 10] + } + ], + { + hovermode: 'x', + xaxis: { range: ['2019-02-06', '2019-12-01'] }, + margin: { l: 0, r: 0 }, + width: 400, + height: 400 + } + ) + .then(_hoverLeft) + .then(_assert('left-edge hover', { txt: 'Jan 1, 2019', lx: 37 })) + .then(_hoverRight) + .then(_assert('right-edge hover', { txt: 'Jan 1, 2020', lx: 362 })) + .then(function () { + return Plotly.relayout(gd, 'xaxis.side', 'top'); + }) + .then(_hoverLeft) + .then(_assert('left-edge hover (side:top)', { txt: 'Jan 1, 2019', lx: 37 })) + .then(_hoverRight) + .then(_assert('right-edge hover (side:top)', { txt: 'Jan 1, 2020', lx: 362 })) + .then(done, done.fail); }); - it('hovermode:y common label should shift and clip text start into graph div', function(done) { + it('hovermode:y common label should shift and clip text start into graph div', function (done) { function _assert(msg, exp) { - return function() { + return function () { var label = d3Select('g.axistext'); - if(label.node()) { + if (label.node()) { var ltext = label.select('text'); expect(ltext.text()).toBe(exp.txt, 'common label text| ' + msg); expect(ltext.attr('x')).toBeWithin(exp.ltx, 5, 'common label text x| ' + msg); @@ -2024,10 +2242,10 @@ describe('hover info', function() { var clipPath = d3Select('#' + clipId); negateIf(exp.clip, expect(clipPath.node())).toBe(null, 'text clip path|' + msg); - if(exp.tspanX) { + if (exp.tspanX) { var tspans = label.selectAll('tspan'); - if(tspans.size()) { - tspans.each(function(d, i) { + if (tspans.size()) { + tspans.each(function (d, i) { var s = d3Select(this); expect(s.attr('x')).toBeWithin(exp.tspanX[i], 5, i + '- tspan shift| ' + msg); }); @@ -2041,76 +2259,88 @@ describe('hover info', function() { }; } - function _hoverWayLong() { return _hover(gd, 135, 100); } + function _hoverWayLong() { + return _hover(gd, 135, 100); + } - function _hoverA() { return _hover(gd, 135, 20); } + function _hoverA() { + return _hover(gd, 135, 20); + } - Plotly.newPlot(gd, [{ - type: 'bar', - orientation: 'h', - y: ['Looong label', 'Loooooger label', 'Waay loooong label', 'a'], - x: [2, 5, 10, 2] - }], { - hovermode: 'y', - width: 400, - height: 400 - }) - .then(_hoverWayLong) - .then(_assert('on way long label', {txt: 'Waay loooong label', clip: true, ltx: 38})) - .then(_hoverA) - .then(_assert('on "a" label', {txt: 'a', clip: false, ltx: -9})) - .then(function() { - return Plotly.restyle(gd, { - y: [['Looong label', 'Loooooger label', 'SHORT!
Waay loooong label', 'a']] - }); - }) - .then(_hoverWayLong) - .then(_assert('on way long label (multi-line case)', { - txt: 'SHORT!Waay loooong label', - clip: true, - ltx: 38, - tspanX: [-11, 38] - })) - .then(done, done.fail); + Plotly.newPlot( + gd, + [ + { + type: 'bar', + orientation: 'h', + y: ['Looong label', 'Loooooger label', 'Waay loooong label', 'a'], + x: [2, 5, 10, 2] + } + ], + { + hovermode: 'y', + width: 400, + height: 400 + } + ) + .then(_hoverWayLong) + .then(_assert('on way long label', { txt: 'Waay loooong label', clip: true, ltx: 38 })) + .then(_hoverA) + .then(_assert('on "a" label', { txt: 'a', clip: false, ltx: -9 })) + .then(function () { + return Plotly.restyle(gd, { + y: [['Looong label', 'Loooooger label', 'SHORT!
Waay loooong label', 'a']] + }); + }) + .then(_hoverWayLong) + .then( + _assert('on way long label (multi-line case)', { + txt: 'SHORT!Waay loooong label', + clip: true, + ltx: 38, + tspanX: [-11, 38] + }) + ) + .then(done, done.fail); }); }); - describe('hovertemplate', function() { + describe('hovertemplate', function () { var mockCopy; - beforeEach(function(done) { + beforeEach(function (done) { mockCopy = Lib.extendDeep({}, mock); Plotly.newPlot(createGraphDiv(), mockCopy.data, mockCopy.layout).then(done); }); - afterEach(function() { + afterEach(function () { Plotly.purge('graph'); destroyGraphDiv(); }); - it('should format labels according to a template string', function(done) { + it('should format labels according to a template string', function (done) { var gd = document.getElementById('graph'); Plotly.restyle(gd, 'hovertemplate', '%{y:$.2f}trace 0') - .then(function() { - Fx.hover('graph', evt, 'xy'); + .then(function () { + Fx.hover('graph', evt, 'xy'); - var hoverTrace = gd._hoverdata[0]; + var hoverTrace = gd._hoverdata[0]; - expect(hoverTrace.curveNumber).toEqual(0); - expect(hoverTrace.pointNumber).toEqual(17); - expect(hoverTrace.x).toEqual(0.388); - expect(hoverTrace.y).toEqual(1); + expect(hoverTrace.curveNumber).toEqual(0); + expect(hoverTrace.pointNumber).toEqual(17); + expect(hoverTrace.x).toEqual(0.388); + expect(hoverTrace.y).toEqual(1); - assertHoverLabelContent({ - nums: '$1.00', - name: 'trace 0', - axis: '0.388' - }); - }) - .then(done, done.fail); + assertHoverLabelContent({ + nums: '$1.00', + name: 'trace 0', + axis: '0.388' + }); + }) + .then(done, done.fail); }); - it('should format labels according to a template string and locale', function(done) { + it('should format labels according to a template string and locale', function (done) { var gd = document.getElementById('graph'); mockCopy.layout.separators = undefined; Plotly.newPlot(gd, mockCopy.data, mockCopy.layout, { @@ -2126,217 +2356,237 @@ describe('hover info', function() { } } }) - .then(function() { - return Plotly.restyle(gd, 'hovertemplate', '%{y:$010,.2f}trace 0'); - }) - .then(function() { - Fx.hover('graph', evt, 'xy'); + .then(function () { + return Plotly.restyle(gd, 'hovertemplate', '%{y:$010,.2f}trace 0'); + }) + .then(function () { + Fx.hover('graph', evt, 'xy'); - var hoverTrace = gd._hoverdata[0]; + var hoverTrace = gd._hoverdata[0]; - expect(hoverTrace.curveNumber).toEqual(0); - expect(hoverTrace.pointNumber).toEqual(17); - expect(hoverTrace.x).toEqual(0.388); - expect(hoverTrace.y).toEqual(1); + expect(hoverTrace.curveNumber).toEqual(0); + expect(hoverTrace.pointNumber).toEqual(17); + expect(hoverTrace.x).toEqual(0.388); + expect(hoverTrace.y).toEqual(1); - assertHoverLabelContent({ - nums: '€000 001,00', - name: 'trace 0', - axis: '0,388' - }); - }) - .then(done, done.fail); + assertHoverLabelContent({ + nums: '€000 001,00', + name: 'trace 0', + axis: '0,388' + }); + }) + .then(done, done.fail); }); - it('should format secondary label with extra tag', function(done) { + it('should format secondary label with extra tag', function (done) { var gd = document.getElementById('graph'); Plotly.restyle(gd, 'hovertemplate', 'trace 20 %{y:$.2f}') - .then(function() { - Fx.hover('graph', evt, 'xy'); + .then(function () { + Fx.hover('graph', evt, 'xy'); - var hoverTrace = gd._hoverdata[0]; + var hoverTrace = gd._hoverdata[0]; - expect(hoverTrace.curveNumber).toEqual(0); - expect(hoverTrace.pointNumber).toEqual(17); - expect(hoverTrace.x).toEqual(0.388); - expect(hoverTrace.y).toEqual(1); + expect(hoverTrace.curveNumber).toEqual(0); + expect(hoverTrace.pointNumber).toEqual(17); + expect(hoverTrace.x).toEqual(0.388); + expect(hoverTrace.y).toEqual(1); - assertHoverLabelContent({ - nums: '', - name: 'trace 20 $1.00', - axis: '0.388' - }); - }) - .then(done, done.fail); + assertHoverLabelContent({ + nums: '', + name: 'trace 20 $1.00', + axis: '0.388' + }); + }) + .then(done, done.fail); }); - it('should support pseudo-html', function(done) { + it('should support pseudo-html', function (done) { var gd = document.getElementById('graph'); Plotly.restyle(gd, 'hovertemplate', '%{y:$.2f}
%{fullData.name}') - .then(function() { - Fx.hover('graph', evt, 'xy'); + .then(function () { + Fx.hover('graph', evt, 'xy'); - var hoverTrace = gd._hoverdata[0]; + var hoverTrace = gd._hoverdata[0]; - expect(hoverTrace.curveNumber).toEqual(0); - expect(hoverTrace.pointNumber).toEqual(17); - expect(hoverTrace.x).toEqual(0.388); - expect(hoverTrace.y).toEqual(1); + expect(hoverTrace.curveNumber).toEqual(0); + expect(hoverTrace.pointNumber).toEqual(17); + expect(hoverTrace.x).toEqual(0.388); + expect(hoverTrace.y).toEqual(1); - assertHoverLabelContent({ - nums: '$1.00\nPV learning curve.txt', - name: '', - axis: '0.388' - }); - }) - .then(done, done.fail); + assertHoverLabelContent({ + nums: '$1.00\nPV learning curve.txt', + name: '', + axis: '0.388' + }); + }) + .then(done, done.fail); }); - it('should support array', function(done) { + it('should support array', function (done) { var gd = document.getElementById('graph'); var templates = []; - for(var i = 0; i < mockCopy.data[0].y.length; i++) { + for (var i = 0; i < mockCopy.data[0].y.length; i++) { templates[i] = 'hovertemplate ' + i + ':%{y:$.2f}'; } Plotly.restyle(gd, 'hovertemplate', [templates]) - .then(function() { - Fx.hover('graph', evt, 'xy'); + .then(function () { + Fx.hover('graph', evt, 'xy'); - var hoverTrace = gd._hoverdata[0]; + var hoverTrace = gd._hoverdata[0]; - expect(hoverTrace.curveNumber).toEqual(0); - expect(hoverTrace.pointNumber).toEqual(17); - expect(hoverTrace.x).toEqual(0.388); - expect(hoverTrace.y).toEqual(1); + expect(hoverTrace.curveNumber).toEqual(0); + expect(hoverTrace.pointNumber).toEqual(17); + expect(hoverTrace.x).toEqual(0.388); + expect(hoverTrace.y).toEqual(1); - assertHoverLabelContent({ - nums: 'hovertemplate 17:$1.00', - name: '', - axis: '0.388' - }); - }) - .then(done, done.fail); + assertHoverLabelContent({ + nums: 'hovertemplate 17:$1.00', + name: '', + axis: '0.388' + }); + }) + .then(done, done.fail); }); - it('should contain the axis names', function(done) { + it('should contain the axis names', function (done) { var gd = document.getElementById('graph'); - Plotly.restyle(gd, 'hovertemplate', - '%{yaxis.title.text}:%{y:$.2f}
%{xaxis.title.text}:%{x:0.4f}') - .then(function() { - Fx.hover('graph', evt, 'xy'); + Plotly.restyle( + gd, + 'hovertemplate', + '%{yaxis.title.text}:%{y:$.2f}
%{xaxis.title.text}:%{x:0.4f}' + ) + .then(function () { + Fx.hover('graph', evt, 'xy'); - var hoverTrace = gd._hoverdata[0]; + var hoverTrace = gd._hoverdata[0]; - expect(hoverTrace.curveNumber).toEqual(0); - expect(hoverTrace.pointNumber).toEqual(17); - expect(hoverTrace.x).toEqual(0.388); - expect(hoverTrace.y).toEqual(1); + expect(hoverTrace.curveNumber).toEqual(0); + expect(hoverTrace.pointNumber).toEqual(17); + expect(hoverTrace.x).toEqual(0.388); + expect(hoverTrace.y).toEqual(1); - assertHoverLabelContent({ - nums: 'Cost ($/W​P):$1.00\nCumulative Production (GW):0.3880', - name: '', - axis: '0.388' - }); - }) - .then(done, done.fail); + assertHoverLabelContent({ + nums: 'Cost ($/W​P):$1.00\nCumulative Production (GW):0.3880', + name: '', + axis: '0.388' + }); + }) + .then(done, done.fail); }); - it('should work with layout.meta references', function(done) { + it('should work with layout.meta references', function (done) { var gd = document.getElementById('graph'); - Plotly.update(gd, - {hovertemplate: 'TRACE -- %{meta[0]}%{meta[1]}'}, - {meta: ['A', '$$$']} - ).then(function() { - Fx.hover('graph', evt, 'xy'); + Plotly.update(gd, { hovertemplate: 'TRACE -- %{meta[0]}%{meta[1]}' }, { meta: ['A', '$$$'] }) + .then(function () { + Fx.hover('graph', evt, 'xy'); - assertHoverLabelContent({ - nums: 'TRACE -- A', - name: '$$$', - axis: '0.388' - }); - }) - .then(done, done.fail); + assertHoverLabelContent({ + nums: 'TRACE -- A', + name: '$$$', + axis: '0.388' + }); + }) + .then(done, done.fail); }); - it('should work with trace meta references', function(done) { + it('should work with trace meta references', function (done) { var gd = document.getElementById('graph'); Plotly.update(gd, { - meta: {yname: 'Yy', xname: 'Xx'}, + meta: { yname: 'Yy', xname: 'Xx' }, hovertemplate: 'TRACE -- %{meta.yname}%{meta.xname}' }) - .then(function() { - Fx.hover('graph', evt, 'xy'); + .then(function () { + Fx.hover('graph', evt, 'xy'); - assertHoverLabelContent({ - nums: 'TRACE -- Yy', - name: 'Xx', - axis: '0.388' - }); - }) - .then(done, done.fail); + assertHoverLabelContent({ + nums: 'TRACE -- Yy', + name: 'Xx', + axis: '0.388' + }); + }) + .then(done, done.fail); }); }); - it('should work with trace.name linked to layout.meta', function(done) { + it('should work with trace.name linked to layout.meta', function (done) { var gd = createGraphDiv(); - Plotly.newPlot(gd, [{ - y: [1, 1, 1], - name: '%{meta[0]}', - marker: {size: 40} - }, { - y: [1] - }], { - hovermode: 'x', - meta: ['yo!'], - width: 400, - height: 400 - }) - .then(function() { _hoverNatural(gd, 200, 200); }) - .then(function() { - assertHoverLabelContent({ - nums: '1', - name: 'yo!', - axis: '2' - }); - }) - .then(done, done.fail); + Plotly.newPlot( + gd, + [ + { + y: [1, 1, 1], + name: '%{meta[0]}', + marker: { size: 40 } + }, + { + y: [1] + } + ], + { + hovermode: 'x', + meta: ['yo!'], + width: 400, + height: 400 + } + ) + .then(function () { + _hoverNatural(gd, 200, 200); + }) + .then(function () { + assertHoverLabelContent({ + nums: '1', + name: 'yo!', + axis: '2' + }); + }) + .then(done, done.fail); }); - it('should fallback to regular hover content when hoveron does not support hovertemplate', function(done) { + it('should fallback to regular hover content when hoveron does not support hovertemplate', function (done) { var gd = createGraphDiv(); var fig = Lib.extendDeep({}, require('../../image/mocks/scatter_fill_self_next.json')); - fig.data.forEach(function(trace) { + fig.data.forEach(function (trace) { trace.hoveron = 'points+fills'; trace.hovertemplate = '%{x} | %{y}'; }); fig.layout.hovermode = 'closest'; fig.layout.showlegend = false; - fig.layout.margin = {t: 0, b: 0, l: 0, r: 0}; + fig.layout.margin = { t: 0, b: 0, l: 0, r: 0 }; Plotly.newPlot(gd, fig) - .then(function() { _hoverNatural(gd, 180, 200); }) - .then(function() { - assertHoverLabelContent({ - nums: 'trace 1', - name: '' - }, 'hovering on a fill'); - }) - .then(function() { _hoverNatural(gd, 50, 95); }) - .then(function() { - assertHoverLabelContent({ - nums: '0 | 5', - name: 'trace 1' - }, 'hovering on a pt'); - }) - .then(done, done.fail); + .then(function () { + _hoverNatural(gd, 180, 200); + }) + .then(function () { + assertHoverLabelContent( + { + nums: 'trace 1', + name: '' + }, + 'hovering on a fill' + ); + }) + .then(function () { + _hoverNatural(gd, 50, 95); + }) + .then(function () { + assertHoverLabelContent( + { + nums: '0 | 5', + name: 'trace 1' + }, + 'hovering on a pt' + ); + }) + .then(done, done.fail); }); - it('should honor *hoverlabel.align', function(done) { + it('should honor *hoverlabel.align', function (done) { var gd = createGraphDiv(); function _assert(msg, exp) { @@ -2345,45 +2595,83 @@ describe('hover info', function() { expect(Number(tx.attr('x'))).toBeWithin(exp.posX, 3, 'x position|' + msg); } - Plotly.newPlot(gd, [{ - y: [1, 2, 1], - text: 'LONG TEXT' - }], { - xaxis: {range: [0, 2]}, - margin: {l: 0, t: 0, b: 0, r: 0}, - hovermode: 'closest', - width: 400, - height: 400 - }) - .then(function() { _hoverNatural(gd, 0, 395); }) - .then(function() { _assert('base left pt', {textAnchor: 'start', posX: 9}); }) - .then(function() { _hoverNatural(gd, 395, 395); }) - .then(function() { _assert('base right pt', {textAnchor: 'end', posX: -9}); }) - .then(function() { - return Plotly.relayout(gd, 'hoverlabel.align', 'left'); - }) - .then(function() { _hoverNatural(gd, 0, 395); }) - .then(function() { _assert('align:left left pt', {textAnchor: 'start', posX: 9}); }) - .then(function() { _hoverNatural(gd, 395, 395); }) - .then(function() { _assert('align:left right pt', {textAnchor: 'start', posX: -84.73}); }) - .then(function() { - return Plotly.restyle(gd, 'hoverlabel.align', 'right'); - }) - .then(function() { _hoverNatural(gd, 0, 395); }) - .then(function() { _assert('align:right left pt', {textAnchor: 'end', posX: 84.73}); }) - .then(function() { _hoverNatural(gd, 395, 395); }) - .then(function() { _assert('align:right right pt', {textAnchor: 'end', posX: -9}); }) - .then(function() { - return Plotly.restyle(gd, 'hoverlabel.align', [['right', 'auto', 'left']]); - }) - .then(function() { _hoverNatural(gd, 0, 395); }) - .then(function() { _assert('arrayOk align:right left pt', {textAnchor: 'end', posX: 84.73}); }) - .then(function() { _hoverNatural(gd, 395, 395); }) - .then(function() { _assert('arrayOk align:left right pt', {textAnchor: 'start', posX: -84.73}); }) - .then(done, done.fail); + Plotly.newPlot( + gd, + [ + { + y: [1, 2, 1], + text: 'LONG TEXT' + } + ], + { + xaxis: { range: [0, 2] }, + margin: { l: 0, t: 0, b: 0, r: 0 }, + hovermode: 'closest', + width: 400, + height: 400 + } + ) + .then(function () { + _hoverNatural(gd, 0, 395); + }) + .then(function () { + _assert('base left pt', { textAnchor: 'start', posX: 9 }); + }) + .then(function () { + _hoverNatural(gd, 395, 395); + }) + .then(function () { + _assert('base right pt', { textAnchor: 'end', posX: -9 }); + }) + .then(function () { + return Plotly.relayout(gd, 'hoverlabel.align', 'left'); + }) + .then(function () { + _hoverNatural(gd, 0, 395); + }) + .then(function () { + _assert('align:left left pt', { textAnchor: 'start', posX: 9 }); + }) + .then(function () { + _hoverNatural(gd, 395, 395); + }) + .then(function () { + _assert('align:left right pt', { textAnchor: 'start', posX: -84.73 }); + }) + .then(function () { + return Plotly.restyle(gd, 'hoverlabel.align', 'right'); + }) + .then(function () { + _hoverNatural(gd, 0, 395); + }) + .then(function () { + _assert('align:right left pt', { textAnchor: 'end', posX: 84.73 }); + }) + .then(function () { + _hoverNatural(gd, 395, 395); + }) + .then(function () { + _assert('align:right right pt', { textAnchor: 'end', posX: -9 }); + }) + .then(function () { + return Plotly.restyle(gd, 'hoverlabel.align', [['right', 'auto', 'left']]); + }) + .then(function () { + _hoverNatural(gd, 0, 395); + }) + .then(function () { + _assert('arrayOk align:right left pt', { textAnchor: 'end', posX: 84.73 }); + }) + .then(function () { + _hoverNatural(gd, 395, 395); + }) + .then(function () { + _assert('arrayOk align:left right pt', { textAnchor: 'start', posX: -84.73 }); + }) + .then(done, done.fail); }); - it('should honor *hoverlabel.align (centered label case)', function(done) { + it('should honor *hoverlabel.align (centered label case)', function (done) { var gd = createGraphDiv(); function _assert(msg, exp) { @@ -2393,79 +2681,100 @@ describe('hover info', function() { delete gd._hoverdata; } - Plotly.newPlot(gd, [{ - x: ['giraffes'], - y: [5], - name: 'LA Zoo', - type: 'bar', - text: ['Way tooooo long hover info!'], - hoverinfo: 'all' - }], { - margin: {l: 0, t: 0, b: 0, r: 0}, - hovermode: 'closest', - width: 400, - height: 400 - }) - .then(function() { _hoverNatural(gd, 200, 200); }) - .then(function() { _assert('base', {textAnchor: 'middle', posX: -24.3}); }) - .then(function() { - return Plotly.relayout(gd, 'hoverlabel.align', 'left'); - }) - .then(function() { _hoverNatural(gd, 200, 200); }) - .then(function() { _assert('align:left', {textAnchor: 'start', posX: -105.7}); }) - .then(function() { - return Plotly.restyle(gd, 'hoverlabel.align', 'right'); - }) - .then(function() { _hoverNatural(gd, 200, 200); }) - .then(function() { _assert('align:right', {textAnchor: 'end', posX: 57}); }) - .then(done, done.fail); + Plotly.newPlot( + gd, + [ + { + x: ['giraffes'], + y: [5], + name: 'LA Zoo', + type: 'bar', + text: ['Way tooooo long hover info!'], + hoverinfo: 'all' + } + ], + { + margin: { l: 0, t: 0, b: 0, r: 0 }, + hovermode: 'closest', + width: 400, + height: 400 + } + ) + .then(function () { + _hoverNatural(gd, 200, 200); + }) + .then(function () { + _assert('base', { textAnchor: 'middle', posX: -24.3 }); + }) + .then(function () { + return Plotly.relayout(gd, 'hoverlabel.align', 'left'); + }) + .then(function () { + _hoverNatural(gd, 200, 200); + }) + .then(function () { + _assert('align:left', { textAnchor: 'start', posX: -105.7 }); + }) + .then(function () { + return Plotly.restyle(gd, 'hoverlabel.align', 'right'); + }) + .then(function () { + _hoverNatural(gd, 200, 200); + }) + .then(function () { + _assert('align:right', { textAnchor: 'end', posX: 57 }); + }) + .then(done, done.fail); }); }); -describe('hover info on stacked subplots', function() { +describe('hover info on stacked subplots', function () { 'use strict'; afterEach(destroyGraphDiv); - describe('hover info on stacked subplots with shared x-axis', function() { - var mock = Lib.extendDeep({}, - require('../../image/mocks/stacked_coupled_subplots.json'), - {data: [ + describe('hover info on stacked subplots with shared x-axis', function () { + var mock = Lib.extendDeep({}, require('../../image/mocks/stacked_coupled_subplots.json'), { + data: [ // Tweak the mock so the higher subplot sometimes has points // higher *within the subplot*, sometimes lower. // This was the problem in #2370 - {}, {y: [100, 120, 100]} - ]}); + {}, + { y: [100, 120, 100] } + ] + }); mock.layout.hovermode = 'x'; var gd; - beforeEach(function(done) { + beforeEach(function (done) { gd = createGraphDiv(); Plotly.newPlot(gd, mock.data, mock.layout).then(done); }); function _check(xval, ptSpec1, ptSpec2) { Lib.clearThrottle(); - Plotly.Fx.hover(gd, {xval: xval}, ['xy', 'xy2', 'xy3']); + Plotly.Fx.hover(gd, { xval: xval }, ['xy', 'xy2', 'xy3']); expect(gd._hoverdata.length).toBe(2); - expect(gd._hoverdata[0]).toEqual(jasmine.objectContaining( - { + expect(gd._hoverdata[0]).toEqual( + jasmine.objectContaining({ curveNumber: ptSpec1[0], pointNumber: ptSpec1[1], x: xval, y: ptSpec1[2] - })); + }) + ); - expect(gd._hoverdata[1]).toEqual(jasmine.objectContaining( - { + expect(gd._hoverdata[1]).toEqual( + jasmine.objectContaining({ curveNumber: ptSpec2[0], pointNumber: ptSpec2[1], x: xval, y: ptSpec2[2] - })); + }) + ); assertHoverLabelContent({ // There should be 2 pts being hovered over, @@ -2479,64 +2788,71 @@ describe('hover info on stacked subplots', function() { // ensure the hover label bounding boxes don't overlap, except a little margin of 5 px // testing #2370 var bBoxes = []; - d3SelectAll('g.hovertext').each(function() { + d3SelectAll('g.hovertext').each(function () { bBoxes.push(this.getBoundingClientRect()); }); expect(bBoxes.length).toBe(2); var disjointY = bBoxes[0].top >= bBoxes[1].bottom - 5 || bBoxes[1].top >= bBoxes[0].bottom - 5; - expect(disjointY).toBe(true, bBoxes.map(function(bb) { return {top: bb.top, bottom: bb.bottom}; })); + expect(disjointY).toBe( + true, + bBoxes.map(function (bb) { + return { top: bb.top, bottom: bb.bottom }; + }) + ); } - it('responds to hover and keeps the labels from crossing', function() { + it('responds to hover and keeps the labels from crossing', function () { _check(2, [0, 2, 12, 'trace 0'], [1, 0, 100, 'trace 1']); _check(3, [1, 1, 120, 'trace 1'], [2, 0, 1000, 'trace 2']); _check(4, [1, 2, 100, 'trace 1'], [2, 1, 1100, 'trace 2']); }); }); - describe('hover info on stacked subplots with shared y-axis', function() { + describe('hover info on stacked subplots with shared y-axis', function () { var mock = Lib.extendDeep(require('../../image/mocks/stacked_subplots_shared_yaxis.json')); mock.data[0].name = 'Who put the bomp in the bomp bah bomp bah bomp'; - mock.data[0].hoverlabel = {namelength: -1}; + mock.data[0].hoverlabel = { namelength: -1 }; mock.data[1].name = 'Who put the ram in the rama lama ding dong'; - mock.data[1].hoverlabel = {namelength: [2, 4]}; + mock.data[1].hoverlabel = { namelength: [2, 4] }; mock.data[2].name = 'Who put the bop in the bop shoo bop shoo bop'; - mock.layout.hoverlabel = {namelength: 10}; + mock.layout.hoverlabel = { namelength: 10 }; - beforeEach(function(done) { + beforeEach(function (done) { Plotly.newPlot(createGraphDiv(), mock.data, mock.layout).then(done); }); - it('responds to hover', function() { + it('responds to hover', function () { var gd = document.getElementById('graph'); - Plotly.Fx.hover(gd, {yval: 0}, ['xy', 'x2y', 'x3y']); + Plotly.Fx.hover(gd, { yval: 0 }, ['xy', 'x2y', 'x3y']); expect(gd._hoverdata.length).toEqual(3); - expect(gd._hoverdata[0]).toEqual(jasmine.objectContaining( - { + expect(gd._hoverdata[0]).toEqual( + jasmine.objectContaining({ curveNumber: 0, pointNumber: 0, x: 1, y: 0 - })); + }) + ); - expect(gd._hoverdata[2]).toEqual(jasmine.objectContaining( - { + expect(gd._hoverdata[2]).toEqual( + jasmine.objectContaining({ curveNumber: 1, pointNumber: 0, x: 2.1, y: 0 - })); + }) + ); - expect(gd._hoverdata[1]).toEqual(jasmine.objectContaining( - { + expect(gd._hoverdata[1]).toEqual( + jasmine.objectContaining({ curveNumber: 2, pointNumber: 0, x: 3, y: 0 - })); - + }) + ); assertHoverLabelContent({ // There should be three points being hovered over, in three different traces, @@ -2550,7 +2866,7 @@ describe('hover info on stacked subplots', function() { }); }); -describe('hover on subplots when hoversubplots is set to *single* and x hovermodes', function() { +describe('hover on subplots when hoversubplots is set to *single* and x hovermodes', function () { 'use strict'; var mock = { @@ -2571,23 +2887,23 @@ describe('hover on subplots when hoversubplots is set to *single* and x hovermod y: [1, 3, 2], yaxis: 'y2' } - ], + ] }; var gd; - beforeEach(function(done) { + beforeEach(function (done) { gd = createGraphDiv(); Plotly.newPlot(gd, mock).then(done); }); afterEach(destroyGraphDiv); - it('hovermode: *x* | *x unified* with hoversubplots: *axis*', function() { + it('hovermode: *x* | *x unified* with hoversubplots: *axis*', function () { var pos = 0; var subplot = 'xy'; Lib.clearThrottle(); - Plotly.Fx.hover(gd, {xval: pos}, subplot); + Plotly.Fx.hover(gd, { xval: pos }, subplot); expect(gd._hoverdata.length).toBe(1); assertHoverLabelContent({ nums: '1', @@ -2598,7 +2914,7 @@ describe('hover on subplots when hoversubplots is set to *single* and x hovermod pos = 0; subplot = 'xy2'; Lib.clearThrottle(); - Plotly.Fx.hover(gd, {xval: pos}, subplot); + Plotly.Fx.hover(gd, { xval: pos }, subplot); expect(gd._hoverdata.length).toBe(1); assertHoverLabelContent({ nums: '1', @@ -2610,7 +2926,7 @@ describe('hover on subplots when hoversubplots is set to *single* and x hovermod pos = 0; subplot = 'xy'; Lib.clearThrottle(); - Plotly.Fx.hover(gd, {xval: pos}, subplot); + Plotly.Fx.hover(gd, { xval: pos }, subplot); expect(gd._hoverdata.length).toBe(1); }); }); @@ -2621,7 +2937,7 @@ function assertFirstPointOn(gd, xaxisId, yaxisId) { expect(gd._hoverdata[0].yaxis._id).toBe(yaxisId); } -describe('hover on subplots when hoversubplots is set to *axis* and x hovermodes', function() { +describe('hover on subplots when hoversubplots is set to *axis* and x hovermodes', function () { 'use strict'; var mock = { @@ -2652,23 +2968,23 @@ describe('hover on subplots when hoversubplots is set to *axis* and x hovermodes xaxis: 'x2', yaxis: 'y2' } - ], + ] }; var gd; - beforeEach(function(done) { + beforeEach(function (done) { gd = createGraphDiv(); Plotly.newPlot(gd, mock).then(done); }); afterEach(destroyGraphDiv); - it('hovermode: *x* | *x unified* with hoversubplots: *axis*', function() { + it('hovermode: *x* | *x unified* with hoversubplots: *axis*', function () { var pos = 0; var subplot = 'xy'; Lib.clearThrottle(); - Plotly.Fx.hover(gd, {xval: pos}, subplot); + Plotly.Fx.hover(gd, { xval: pos }, subplot); expect(gd._hoverdata.length).toBe(3); assertFirstPointOn(gd, 'x', 'y'); @@ -2682,7 +2998,7 @@ describe('hover on subplots when hoversubplots is set to *axis* and x hovermodes pos = 1; subplot = 'xy2'; Lib.clearThrottle(); - Plotly.Fx.hover(gd, {xval: pos}, subplot); + Plotly.Fx.hover(gd, { xval: pos }, subplot); assertFirstPointOn(gd, 'x', 'y2'); @@ -2696,7 +3012,7 @@ describe('hover on subplots when hoversubplots is set to *axis* and x hovermodes pos = 2; subplot = 'xy3'; Lib.clearThrottle(); - Plotly.Fx.hover(gd, {xval: pos}, subplot); + Plotly.Fx.hover(gd, { xval: pos }, subplot); assertFirstPointOn(gd, 'x', 'y3'); @@ -2711,14 +3027,14 @@ describe('hover on subplots when hoversubplots is set to *axis* and x hovermodes pos = 0; subplot = 'xy'; Lib.clearThrottle(); - Plotly.Fx.hover(gd, {xval: pos}, subplot); + Plotly.Fx.hover(gd, { xval: pos }, subplot); expect(gd._hoverdata.length).toBe(3); assertFirstPointOn(gd, 'x', 'y'); }); }); -describe('hover on subplots when hoversubplots is set to *axis* and y hovermodes', function() { +describe('hover on subplots when hoversubplots is set to *axis* and y hovermodes', function () { 'use strict'; var mock = { @@ -2749,23 +3065,23 @@ describe('hover on subplots when hoversubplots is set to *axis* and y hovermodes xaxis: 'x2', yaxis: 'y2' } - ], + ] }; var gd; - beforeEach(function(done) { + beforeEach(function (done) { gd = createGraphDiv(); Plotly.newPlot(gd, mock).then(done); }); afterEach(destroyGraphDiv); - it('hovermode: *y* | *y unified* with hoversubplots: *axis*', function() { + it('hovermode: *y* | *y unified* with hoversubplots: *axis*', function () { var pos = 0; var subplot = 'xy'; Lib.clearThrottle(); - Plotly.Fx.hover(gd, {yval: pos}, subplot); + Plotly.Fx.hover(gd, { yval: pos }, subplot); expect(gd._hoverdata.length).toBe(3); assertFirstPointOn(gd, 'x', 'y'); @@ -2779,7 +3095,7 @@ describe('hover on subplots when hoversubplots is set to *axis* and y hovermodes pos = 1; subplot = 'x2y'; Lib.clearThrottle(); - Plotly.Fx.hover(gd, {yval: pos}, subplot); + Plotly.Fx.hover(gd, { yval: pos }, subplot); expect(gd._hoverdata.length).toBe(3); @@ -2794,7 +3110,7 @@ describe('hover on subplots when hoversubplots is set to *axis* and y hovermodes pos = 2; subplot = 'x3y'; Lib.clearThrottle(); - Plotly.Fx.hover(gd, {yval: pos}, subplot); + Plotly.Fx.hover(gd, { yval: pos }, subplot); expect(gd._hoverdata.length).toBe(3); @@ -2810,14 +3126,14 @@ describe('hover on subplots when hoversubplots is set to *axis* and y hovermodes pos = 0; subplot = 'xy'; Lib.clearThrottle(); - Plotly.Fx.hover(gd, {yval: pos}, subplot); + Plotly.Fx.hover(gd, { yval: pos }, subplot); expect(gd._hoverdata.length).toBe(3); assertFirstPointOn(gd, 'x', 'y'); }); }); -describe('splom hover on subplots when hoversubplots is set to *axis* and (x|y) hovermodes', function() { +describe('splom hover on subplots when hoversubplots is set to *axis* and (x|y) hovermodes', function () { 'use strict'; var mock = Lib.extendDeep({}, splomLogMock); @@ -2826,16 +3142,16 @@ describe('splom hover on subplots when hoversubplots is set to *axis* and (x|y) var gd; - beforeEach(function(done) { + beforeEach(function (done) { gd = createGraphDiv(); Plotly.newPlot(gd, mock).then(done); }); afterEach(destroyGraphDiv); - it('splom hoversubplots: *axis*', function() { + it('splom hoversubplots: *axis*', function () { Lib.clearThrottle(); - Plotly.Fx.hover(gd, {x: 200, y: 200}, 'xy'); + Plotly.Fx.hover(gd, { x: 200, y: 200 }, 'xy'); assertFirstPointOn(gd, 'x', 'y'); expect(gd._hoverdata.length).toBe(2); assertHoverLabelContent({ @@ -2846,34 +3162,41 @@ describe('splom hover on subplots when hoversubplots is set to *axis* and (x|y) Plotly.relayout(gd, 'hovermode', 'x unified'); Lib.clearThrottle(); - Plotly.Fx.hover(gd, {x: 200, y: 200}, 'xy'); + Plotly.Fx.hover(gd, { x: 200, y: 200 }, 'xy'); assertFirstPointOn(gd, 'x', 'y'); expect(gd._hoverdata.length).toBe(2); Plotly.relayout(gd, 'hovermode', 'y unified'); Lib.clearThrottle(); - Plotly.Fx.hover(gd, {x: 200, y: 200}, 'xy'); + Plotly.Fx.hover(gd, { x: 200, y: 200 }, 'xy'); assertFirstPointOn(gd, 'x', 'y'); expect(gd._hoverdata.length).toBe(2); }); }); -describe('splom hover *axis* hoversubplots splom points on same position should pick points with same index', function() { +describe('splom hover *axis* hoversubplots splom points on same position should pick points with same index', function () { 'use strict'; var mock = { - data: [{ - type: 'splom', - dimensions: [{ - values: [1, 1, 1, 1] - }, { - values: [1, 2, 3, 4] - }, { - values: [1, 2, 3, 4] - }, { - values: [1, null, 3, 4] + data: [ + { + type: 'splom', + dimensions: [ + { + values: [1, 1, 1, 1] + }, + { + values: [1, 2, 3, 4] + }, + { + values: [1, 2, 3, 4] + }, + { + values: [1, null, 3, 4] + } + ] } - ]}], + ], layout: { hoversubplots: 'axis', hovermode: 'x', @@ -2884,14 +3207,14 @@ describe('splom hover *axis* hoversubplots splom points on same position should var gd; - beforeEach(function(done) { + beforeEach(function (done) { gd = createGraphDiv(); Plotly.newPlot(gd, mock).then(done); }); afterEach(destroyGraphDiv); - it('splom *axis* hoversubplots', function() { + it('splom *axis* hoversubplots', function () { Lib.clearThrottle(); Plotly.Fx.hover(gd, {}, 'xy'); assertFirstPointOn(gd, 'x', 'y'); @@ -2964,74 +3287,71 @@ describe('splom hover *axis* hoversubplots splom points on same position should }); }); - -describe('hover on many lines+bars', function() { +describe('hover on many lines+bars', function () { 'use strict'; afterEach(destroyGraphDiv); - it('shows hover info for both traces', function(done) { + it('shows hover info for both traces', function (done) { // see https://github.com/plotly/plotly.js/issues/780 var values = new Array(1000); var values2 = new Array(values.length); - for(var i = 0; i < values.length; i++) { + for (var i = 0; i < values.length; i++) { values[i] = i; values2[i] = i * 2; } var gd = createGraphDiv(); - Plotly.newPlot(gd, [ - {y: values2}, - {y: values, type: 'bar'} - ], { + Plotly.newPlot(gd, [{ y: values2 }, { y: values, type: 'bar' }], { hovermode: 'x', width: 400, height: 400, - margin: {l: 100, r: 100, t: 100, b: 100} + margin: { l: 100, r: 100, t: 100, b: 100 } }) - .then(function() { - Lib.clearThrottle(); - mouseEvent('mousemove', 200, 100); - Lib.clearThrottle(); + .then(function () { + Lib.clearThrottle(); + mouseEvent('mousemove', 200, 100); + Lib.clearThrottle(); - expect(d3Select(gd).selectAll('g.hovertext').size()).toBe(2); - expect(d3Select(gd).selectAll('g.axistext').size()).toBe(1); - }) - .then(done, done.fail); + expect(d3Select(gd).selectAll('g.hovertext').size()).toBe(2); + expect(d3Select(gd).selectAll('g.axistext').size()).toBe(1); + }) + .then(done, done.fail); }); }); -describe('hover info on overlaid subplots', function() { +describe('hover info on overlaid subplots', function () { 'use strict'; afterEach(destroyGraphDiv); - it('should respond to hover', function(done) { + it('should respond to hover', function (done) { var mock = require('../../image/mocks/autorange-tozero-rangemode.json'); mock.layout.hovermode = 'x'; - Plotly.newPlot(createGraphDiv(), mock.data, mock.layout).then(function() { - mouseEvent('mousemove', 768, 345); + Plotly.newPlot(createGraphDiv(), mock.data, mock.layout) + .then(function () { + mouseEvent('mousemove', 768, 345); - assertHoverLabelContent({ - nums: ['0.35', '2,352.5'], - name: ['Take Rate', 'Revenue'], - axis: '1' - }); - }) - .then(done, done.fail); + assertHoverLabelContent({ + nums: ['0.35', '2,352.5'], + name: ['Take Rate', 'Revenue'], + axis: '1' + }); + }) + .then(done, done.fail); }); }); -describe('hover after resizing', function() { +describe('hover after resizing', function () { var gd; afterEach(destroyGraphDiv); function _click(pos) { - return new Promise(function(resolve) { + return new Promise(function (resolve) { click(pos[0], pos[1]); - setTimeout(function() { + setTimeout(function () { resolve(); }, HOVERMINTIME); }); @@ -3040,14 +3360,17 @@ describe('hover after resizing', function() { function check(pos, expectation, msg) { Lib.clearThrottle(); mouseEvent('mousemove', pos[0], pos[1]); - assertHoverLabelContent({ - nums: expectation[0], - name: expectation[1], - axis: expectation[2] - }, msg); + assertHoverLabelContent( + { + nums: expectation[0], + name: expectation[1], + axis: expectation[2] + }, + msg + ); } - it('should work', function(done) { + it('should work', function (done) { gd = createGraphDiv(); var data = [{ y: [2, 1, 2] }]; @@ -3056,39 +3379,40 @@ describe('hover after resizing', function() { var pos0 = [305, 403]; var pos1 = [401, 122]; - Plotly.newPlot(gd, data, layout).then(function() { - // to test https://github.com/plotly/plotly.js/issues/1044 - return _click(pos0); - }) - .then(function() { - return check(pos0, ['1', null, '1'], 'before resize, showing pt label'); - }) - .then(function() { - 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, null], 'after resize, not showing blank spot'); - }) - .then(function() { - 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'); - }) - .then(function() { - return check(pos1, [null, null, null], 'back to initial, not showing blank spot'); - }) - .then(done, done.fail); + Plotly.newPlot(gd, data, layout) + .then(function () { + // to test https://github.com/plotly/plotly.js/issues/1044 + return _click(pos0); + }) + .then(function () { + return check(pos0, ['1', null, '1'], 'before resize, showing pt label'); + }) + .then(function () { + 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, null], 'after resize, not showing blank spot'); + }) + .then(function () { + 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'); + }) + .then(function () { + return check(pos1, [null, null, null], 'back to initial, not showing blank spot'); + }) + .then(done, done.fail); }); }); -describe('hover on fill', function() { +describe('hover on fill', function () { 'use strict'; afterEach(destroyGraphDiv); @@ -3108,116 +3432,135 @@ describe('hover on fill', function() { expect(+transformCoords[1]).toBeCloseTo(labelPos[1], -1.2, labelText + ':y'); } - it('should always show one label in the right place', function(done) { + it('should always show one label in the right place', function (done) { var mock = Lib.extendDeep({}, require('../../image/mocks/scatter_fill_self_next.json')); - mock.data.forEach(function(trace) { trace.hoveron = 'fills'; }); + mock.data.forEach(function (trace) { + trace.hoveron = 'fills'; + }); mock.layout.hovermode = 'x'; - Plotly.newPlot(createGraphDiv(), mock.data, mock.layout).then(function() { - assertLabelsCorrect([242, 142], [252, 133.8], 'trace 2'); - assertLabelsCorrect([242, 292], [233, 210], 'trace 1'); - assertLabelsCorrect([147, 252], [158.925, 248.1], 'trace 0'); - }) - .then(done, done.fail); + Plotly.newPlot(createGraphDiv(), mock.data, mock.layout) + .then(function () { + assertLabelsCorrect([242, 142], [252, 133.8], 'trace 2'); + assertLabelsCorrect([242, 292], [233, 210], 'trace 1'); + assertLabelsCorrect([147, 252], [158.925, 248.1], 'trace 0'); + }) + .then(done, done.fail); }); - it('should always show one label in the right place (symmetric fill edge case)', function(done) { + it('should always show one label in the right place (symmetric fill edge case)', function (done) { var gd = createGraphDiv(); - Plotly.newPlot(gd, [{ - x: [6, 7, 8, 7, 6], - y: [5, 4, 5, 6, 5], - fill: 'tonext', - hoveron: 'fills' - }], { - hovermode: 'x', - width: 500, - height: 500, - margin: {l: 50, t: 50, r: 50, b: 50} - }) - .then(function() { - assertLabelsCorrect([200, 200], [73.75, 250], 'trace 0'); + Plotly.newPlot( + gd, + [ + { + x: [6, 7, 8, 7, 6], + y: [5, 4, 5, 6, 5], + fill: 'tonext', + hoveron: 'fills' + } + ], + { + hovermode: 'x', + width: 500, + height: 500, + margin: { l: 50, t: 50, r: 50, b: 50 } + } + ) + .then(function () { + assertLabelsCorrect([200, 200], [73.75, 250], 'trace 0'); - return Plotly.restyle(gd, { - x: [[6, 7, 8, 7]], - y: [[5, 4, 5, 6]] - }); - }) - .then(function() { - // gives same results w/o closing point - assertLabelsCorrect([200, 200], [73.75, 250], 'trace 0'); - }) - .then(done, done.fail); + return Plotly.restyle(gd, { + x: [[6, 7, 8, 7]], + y: [[5, 4, 5, 6]] + }); + }) + .then(function () { + // gives same results w/o closing point + assertLabelsCorrect([200, 200], [73.75, 250], 'trace 0'); + }) + .then(done, done.fail); }); - it('should work for scatterternary too', function(done) { + it('should work for scatterternary too', function (done) { var mock = Lib.extendDeep({}, require('../../image/mocks/ternary_fill.json')); var gd = createGraphDiv(); - Plotly.newPlot(gd, mock.data, mock.layout).then(function() { - expect(gd._fullLayout.hovermode).toBe('closest'); + Plotly.newPlot(gd, mock.data, mock.layout) + .then(function () { + expect(gd._fullLayout.hovermode).toBe('closest'); - // hover over a point when that's closest, even if you're over - // a fill, because by default we have hoveron='points+fills' - assertLabelsCorrect([237, 150], [240.0, 144], - 'trace 2Component A: 0.8Component B: 0.1Component C: 0.1'); + // hover over a point when that's closest, even if you're over + // a fill, because by default we have hoveron='points+fills' + assertLabelsCorrect( + [237, 150], + [240.0, 144], + 'trace 2Component A: 0.8Component B: 0.1Component C: 0.1' + ); - // the rest are hovers over fills - assertLabelsCorrect([237, 170], [247.7, 166], 'trace 2'); - assertLabelsCorrect([237, 218], [266.75, 265], 'trace 1'); - assertLabelsCorrect([237, 240], [247.7, 254], 'trace 0'); + // the rest are hovers over fills + assertLabelsCorrect([237, 170], [247.7, 166], 'trace 2'); + assertLabelsCorrect([237, 218], [266.75, 265], 'trace 1'); + assertLabelsCorrect([237, 240], [247.7, 254], 'trace 0'); - // zoom in to test clipping of large out-of-viewport shapes - return Plotly.relayout(gd, { - 'ternary.aaxis.min': 0.5, - 'ternary.baxis.min': 0.25 - }); - }).then(function() { - // this particular one has a hover label disconnected from the shape itself - // so if we ever fix this, the test will have to be fixed too. - assertLabelsCorrect([295, 218], [275.1, 166], 'trace 2'); - - // trigger an autoscale redraw, which goes through dragElement - return doubleClick(237, 251); - }).then(function() { - // then make sure we can still select a *different* item afterward - assertLabelsCorrect([237, 218], [266.75, 265], 'trace 1'); - }) - .then(done, done.fail); + // zoom in to test clipping of large out-of-viewport shapes + return Plotly.relayout(gd, { + 'ternary.aaxis.min': 0.5, + 'ternary.baxis.min': 0.25 + }); + }) + .then(function () { + // this particular one has a hover label disconnected from the shape itself + // so if we ever fix this, the test will have to be fixed too. + assertLabelsCorrect([295, 218], [275.1, 166], 'trace 2'); + + // trigger an autoscale redraw, which goes through dragElement + return doubleClick(237, 251); + }) + .then(function () { + // then make sure we can still select a *different* item afterward + assertLabelsCorrect([237, 218], [266.75, 265], 'trace 1'); + }) + .then(done, done.fail); }); - it('should act like closest mode on ternary when cartesian is in compare mode', function(done) { + it('should act like closest mode on ternary when cartesian is in compare mode', function (done) { var mock = Lib.extendDeep({}, require('../../image/mocks/ternary_fill.json')); mock.layout.hovermode = 'x'; var gd = createGraphDiv(); - mock.data.push({y: [7, 8, 9]}); - mock.layout.xaxis = {domain: [0.8, 1], visible: false}; - mock.layout.yaxis = {domain: [0.8, 1], visible: false}; + mock.data.push({ y: [7, 8, 9] }); + mock.layout.xaxis = { domain: [0.8, 1], visible: false }; + mock.layout.yaxis = { domain: [0.8, 1], visible: false }; - Plotly.newPlot(gd, mock.data, mock.layout).then(function() { - expect(gd._fullLayout.hovermode).toBe('x'); + Plotly.newPlot(gd, mock.data, mock.layout) + .then(function () { + expect(gd._fullLayout.hovermode).toBe('x'); - // hover over a point when that's closest, even if you're over - // a fill, because by default we have hoveron='points+fills' - assertLabelsCorrect([237, 150], [240.0, 144], - 'trace 2Component A: 0.8Component B: 0.1Component C: 0.1'); + // hover over a point when that's closest, even if you're over + // a fill, because by default we have hoveron='points+fills' + assertLabelsCorrect( + [237, 150], + [240.0, 144], + 'trace 2Component A: 0.8Component B: 0.1Component C: 0.1' + ); - // hovers over fills - assertLabelsCorrect([237, 170], [247.7, 166], 'trace 2'); + // hovers over fills + assertLabelsCorrect([237, 170], [247.7, 166], 'trace 2'); - // hover on the cartesian trace in the corner - assertLabelsCorrect([363, 122], [367, 122], 'trace 38'); - }) - .then(done, done.fail); + // hover on the cartesian trace in the corner + assertLabelsCorrect([363, 122], [367, 122], 'trace 38'); + }) + .then(done, done.fail); }); }); -describe('Hover on multicategory axes', function() { +describe('Hover on multicategory axes', function () { var gd; var eventData; - beforeEach(function() { + beforeEach(function () { gd = createGraphDiv(); }); @@ -3229,107 +3572,129 @@ describe('Hover on multicategory axes', function() { mouseEvent('mousemove', x, y); } - it('should work for bar traces', function(done) { - Plotly.newPlot(gd, [{ - type: 'bar', - x: [['2018', '2018', '2019', '2019'], ['a', 'b', 'a', 'b']], - y: [1, 2, -1, 3] - }], { - hovermode: 'x', - bargap: 0, - width: 400, - height: 400 - }) - .then(function() { - gd.on('plotly_hover', function(d) { - eventData = d.points[0]; - }); - }) - .then(function() { _hover(200, 200); }) - .then(function() { - assertHoverLabelContent({ nums: '−1', axis: '2019 - a' }); - expect(eventData.x).toEqual(['2019', 'a']); - }) - .then(function() { - return Plotly.update(gd, - {hovertemplate: 'Sample: %{x[1]}
Year: %{x[0]}'}, - {hovermode: 'closest'} - ); - }) - .then(function() { _hover(140, 200); }) - .then(function() { - assertHoverLabelContent({ nums: 'Sample: b\nYear: 2018' }); - expect(eventData.x).toEqual(['2018', 'b']); - }) - .then(done, done.fail); + it('should work for bar traces', function (done) { + Plotly.newPlot( + gd, + [ + { + type: 'bar', + x: [ + ['2018', '2018', '2019', '2019'], + ['a', 'b', 'a', 'b'] + ], + y: [1, 2, -1, 3] + } + ], + { + hovermode: 'x', + bargap: 0, + width: 400, + height: 400 + } + ) + .then(function () { + gd.on('plotly_hover', function (d) { + eventData = d.points[0]; + }); + }) + .then(function () { + _hover(200, 200); + }) + .then(function () { + assertHoverLabelContent({ nums: '−1', axis: '2019 - a' }); + expect(eventData.x).toEqual(['2019', 'a']); + }) + .then(function () { + return Plotly.update( + gd, + { hovertemplate: 'Sample: %{x[1]}
Year: %{x[0]}' }, + { hovermode: 'closest' } + ); + }) + .then(function () { + _hover(140, 200); + }) + .then(function () { + assertHoverLabelContent({ nums: 'Sample: b\nYear: 2018' }); + expect(eventData.x).toEqual(['2018', 'b']); + }) + .then(done, done.fail); }); - it('should work on heatmap traces', function(done) { + it('should work on heatmap traces', function (done) { var fig = Lib.extendDeep({}, require('../../image/mocks/heatmap_multicategory.json')); fig.data = [fig.data[0]]; fig.layout.width = 500; fig.layout.height = 500; Plotly.newPlot(gd, fig) - .then(function() { - gd.on('plotly_hover', function(d) { - eventData = d.points[0]; - }); - }) - .then(function() { _hover(200, 200); }) - .then(function() { - assertHoverLabelContent({ - nums: 'x: 2017 - q3\ny: Group 3 - A\nz: 2.303' - }); - expect(eventData.x).toEqual(['2017', 'q3']); - }) - .then(function() { - return Plotly.restyle(gd, 'hovertemplate', '%{z} @ %{x} | %{y}'); - }) - .then(function() { _hover(200, 200); }) - .then(function() { - assertHoverLabelContent({ - nums: '2.303 @ 2017 - q3 | Group 3 - A', - name: 'w/ 2d z' - }); - expect(eventData.x).toEqual(['2017', 'q3']); - }) - .then(done, done.fail); + .then(function () { + gd.on('plotly_hover', function (d) { + eventData = d.points[0]; + }); + }) + .then(function () { + _hover(200, 200); + }) + .then(function () { + assertHoverLabelContent({ + nums: 'x: 2017 - q3\ny: Group 3 - A\nz: 2.303' + }); + expect(eventData.x).toEqual(['2017', 'q3']); + }) + .then(function () { + return Plotly.restyle(gd, 'hovertemplate', '%{z} @ %{x} | %{y}'); + }) + .then(function () { + _hover(200, 200); + }) + .then(function () { + assertHoverLabelContent({ + nums: '2.303 @ 2017 - q3 | Group 3 - A', + name: 'w/ 2d z' + }); + expect(eventData.x).toEqual(['2017', 'q3']); + }) + .then(done, done.fail); }); - it('should work with series', function(done) { + it('should work with series', function (done) { var fig = Lib.extendDeep({}, require('../../image/mocks/multicategory_series.json')); fig.data = [fig.data[0]]; fig.layout.width = 500; fig.layout.height = 500; Plotly.newPlot(gd, fig) - .then(function() { - gd.on('plotly_hover', function(d) { - eventData = d.points[0]; - }); - }) - .then(function() { _hover(200, 200); }) - .then(function() { - expect(eventData.x).toEqual(['High', 4]); - }) - .then(function() { - return Plotly.restyle(gd, 'hovertemplate', '%{z} @ %{x} | %{y}'); - }) - .then(function() { _hover(200, 200); }) - .then(function() { - expect(eventData.x).toEqual(['High', 4]); - }) - .then(done, done.fail); + .then(function () { + gd.on('plotly_hover', function (d) { + eventData = d.points[0]; + }); + }) + .then(function () { + _hover(200, 200); + }) + .then(function () { + expect(eventData.x).toEqual(['High', 4]); + }) + .then(function () { + return Plotly.restyle(gd, 'hovertemplate', '%{z} @ %{x} | %{y}'); + }) + .then(function () { + _hover(200, 200); + }) + .then(function () { + expect(eventData.x).toEqual(['High', 4]); + }) + .then(done, done.fail); }); }); -describe('hover on traces with (x|y)period positioning', function() { +describe('hover on traces with (x|y)period positioning', function () { 'use strict'; var gd; - beforeEach(function() { + beforeEach(function () { gd = createGraphDiv(); }); @@ -3341,790 +3706,876 @@ describe('hover on traces with (x|y)period positioning', function() { mouseEvent('mousemove', x, y); } - it('shows hover info for scatter, bar, waterfall, funnel, heatmap and contour traces', function(done) { + it('shows hover info for scatter, bar, waterfall, funnel, heatmap and contour traces', function (done) { Plotly.newPlot(gd, require('../../image/mocks/period_positioning.json')) - .then(function() { _hover(110, 390); }) - .then(function() { - assertHoverLabelContent({ - name: 'scatter', - nums: '(Jan 2001, 1)' - }); - }) - .then(function() { _hover(290, 285); }) - .then(function() { - assertHoverLabelContent({ - name: 'scatter', - nums: '(Jan 2004, 4)' - }); - }) - .then(function() { _hover(110, 410); }) - .then(function() { - assertHoverLabelContent({ - name: 'bar (v)', - nums: '(Jan 2001, 1)' - }); - }) - .then(function() { _hover(290, 410); }) - .then(function() { - assertHoverLabelContent({ - name: 'bar (v)', - nums: '(Jan 2004, 4)' - }); - }) - .then(function() { _hover(100, 230); }) - .then(function() { - assertHoverLabelContent({ - name: 'bar (h)', - nums: '(1, Jan 2001)' - }); - }) - .then(function() { _hover(100, 120); }) - .then(function() { - assertHoverLabelContent({ - name: 'bar (h)', - nums: '(4, Jan 2004)' - }); - }) - .then(function() { _hover(135, 230); }) - .then(function() { - assertHoverLabelContent({ - name: 'scatter2', - nums: '(1, Jan 2001)' - }); - }) - .then(function() { _hover(305, 120); }) - .then(function() { - assertHoverLabelContent({ - name: 'scatter2', - nums: '(4, Jan 2004)' - }); - }) - .then(function() { _hover(385, 355); }) - .then(function() { - assertHoverLabelContent({ - name: 'waterfall', - nums: [ - '(Jan 2001, 4)', - '4 ▲', - 'Initial: 0' - ].join('\n') - }); - }) - .then(function() { _hover(565, 355); }) - .then(function() { - assertHoverLabelContent({ - name: 'waterfall', - nums: [ - '(Jan 2004, 2)', - '(1) ▼', - 'Initial: 3' - ].join('\n') - }); - }) - .then(function() { _hover(475, 225); }) - .then(function() { - assertHoverLabelContent({ - name: 'funnel', - nums: [ - '(1, Jan 2004)', - '25% of initial', - '50% of previous', - '10% of total' - ].join('\n') - }); - }) - .then(function() { _hover(475, 115); }) - .then(function() { - assertHoverLabelContent({ - name: 'funnel', - nums: [ - '(4, Jan 2001)', - '100% of initial', - '100% of previous', - '40% of total' - ].join('\n') - }); - }) - .then(function() { _hover(665, 365); }) - .then(function() { - assertHoverLabelContent({ - name: 'heatmap', - nums: [ - 'x: Jan 2001', - 'y: Jan 2002', - 'z: 1' - ].join('\n') - }); - }) - .then(function() { _hover(800, 150); }) - .then(function() { - assertHoverLabelContent({ - name: 'contour', - nums: [ - 'x: Jan 2003', - 'y: Jan 2003', - 'z: 0' - ].join('\n') - }); - }) + .then(function () { + _hover(110, 390); + }) + .then(function () { + assertHoverLabelContent({ + name: 'scatter', + nums: '(Jan 2001, 1)' + }); + }) + .then(function () { + _hover(290, 285); + }) + .then(function () { + assertHoverLabelContent({ + name: 'scatter', + nums: '(Jan 2004, 4)' + }); + }) + .then(function () { + _hover(110, 410); + }) + .then(function () { + assertHoverLabelContent({ + name: 'bar (v)', + nums: '(Jan 2001, 1)' + }); + }) + .then(function () { + _hover(290, 410); + }) + .then(function () { + assertHoverLabelContent({ + name: 'bar (v)', + nums: '(Jan 2004, 4)' + }); + }) + .then(function () { + _hover(100, 230); + }) + .then(function () { + assertHoverLabelContent({ + name: 'bar (h)', + nums: '(1, Jan 2001)' + }); + }) + .then(function () { + _hover(100, 120); + }) + .then(function () { + assertHoverLabelContent({ + name: 'bar (h)', + nums: '(4, Jan 2004)' + }); + }) + .then(function () { + _hover(135, 230); + }) + .then(function () { + assertHoverLabelContent({ + name: 'scatter2', + nums: '(1, Jan 2001)' + }); + }) + .then(function () { + _hover(305, 120); + }) + .then(function () { + assertHoverLabelContent({ + name: 'scatter2', + nums: '(4, Jan 2004)' + }); + }) + .then(function () { + _hover(385, 355); + }) + .then(function () { + assertHoverLabelContent({ + name: 'waterfall', + nums: ['(Jan 2001, 4)', '4 ▲', 'Initial: 0'].join('\n') + }); + }) + .then(function () { + _hover(565, 355); + }) + .then(function () { + assertHoverLabelContent({ + name: 'waterfall', + nums: ['(Jan 2004, 2)', '(1) ▼', 'Initial: 3'].join('\n') + }); + }) + .then(function () { + _hover(475, 225); + }) + .then(function () { + assertHoverLabelContent({ + name: 'funnel', + nums: ['(1, Jan 2004)', '25% of initial', '50% of previous', '10% of total'].join('\n') + }); + }) + .then(function () { + _hover(475, 115); + }) + .then(function () { + assertHoverLabelContent({ + name: 'funnel', + nums: ['(4, Jan 2001)', '100% of initial', '100% of previous', '40% of total'].join('\n') + }); + }) + .then(function () { + _hover(665, 365); + }) + .then(function () { + assertHoverLabelContent({ + name: 'heatmap', + nums: ['x: Jan 2001', 'y: Jan 2002', 'z: 1'].join('\n') + }); + }) + .then(function () { + _hover(800, 150); + }) + .then(function () { + assertHoverLabelContent({ + name: 'contour', + nums: ['x: Jan 2003', 'y: Jan 2003', 'z: 0'].join('\n') + }); + }) - .then(done, done.fail); + .then(done, done.fail); }); - it('shows hover info for box, ohlc, candlestick traces', function(done) { + it('shows hover info for box, ohlc, candlestick traces', function (done) { Plotly.newPlot(gd, require('../../image/mocks/period_positioning2.json')) - .then(function() { _hover(110, 390); }) - .then(function() { - assertHoverLabelContent({ - name: 'ohlc', - nums: [ - 'Jan 2001', - 'open: 1', - 'high: 2', - 'low: 0', - 'close: 0.5 ▼' - ].join('\n') - }); - }) - .then(function() { _hover(290, 285); }) - .then(function() { - assertHoverLabelContent({ - name: 'ohlc', - nums: [ - 'Jan 2004', - 'open: 4', - 'high: 8', - 'low: 0', - 'close: 2 ▼' - ].join('\n') - }); - }) - .then(function() { _hover(290, 120); }) - .then(function() { - assertHoverLabelContent({ - name: 'candlestick', - nums: [ - 'Jan 2004', - 'open: 4', - 'high: 8', - 'low: 0', - 'close: 2 ▼' - ].join('\n') - }); - }) - .then(function() { _hover(385, 355); }) - .then(function() { - assertHoverLabelContent({ - name: ['', '', '', 'box (v)', '', '', ''], - nums: [ - '(Jan 2001, min: 2)', - '(Jan 2001, lower fence: 2)', - '(Jan 2001, q1: 4)', - '(Jan 2001, median: 6)', - '(Jan 2001, q3: 8)', - '(Jan 2001, upper fence: 10)', - '(Jan 2001, max: 10)' - ] - }); - }) - .then(function() { _hover(475, 120); }) - .then(function() { - assertHoverLabelContent({ - name: ['', '', '', '', '', '', 'box (h)'], - nums: [ - '(upper fence: 8, Jan 2004)', - '(lower fence: 0, Jan 2004)', - '(max: 8, Jan 2004)', - '(min: 0, Jan 2004)', - '(q1: 2, Jan 2004)', - '(q3: 6, Jan 2004)', - '(median: 4, Jan 2004)' - ] - }); - }) + .then(function () { + _hover(110, 390); + }) + .then(function () { + assertHoverLabelContent({ + name: 'ohlc', + nums: ['Jan 2001', 'open: 1', 'high: 2', 'low: 0', 'close: 0.5 ▼'].join('\n') + }); + }) + .then(function () { + _hover(290, 285); + }) + .then(function () { + assertHoverLabelContent({ + name: 'ohlc', + nums: ['Jan 2004', 'open: 4', 'high: 8', 'low: 0', 'close: 2 ▼'].join('\n') + }); + }) + .then(function () { + _hover(290, 120); + }) + .then(function () { + assertHoverLabelContent({ + name: 'candlestick', + nums: ['Jan 2004', 'open: 4', 'high: 8', 'low: 0', 'close: 2 ▼'].join('\n') + }); + }) + .then(function () { + _hover(385, 355); + }) + .then(function () { + assertHoverLabelContent({ + name: ['', '', '', 'box (v)', '', '', ''], + nums: [ + '(Jan 2001, min: 2)', + '(Jan 2001, lower fence: 2)', + '(Jan 2001, q1: 4)', + '(Jan 2001, median: 6)', + '(Jan 2001, q3: 8)', + '(Jan 2001, upper fence: 10)', + '(Jan 2001, max: 10)' + ] + }); + }) + .then(function () { + _hover(475, 120); + }) + .then(function () { + assertHoverLabelContent({ + name: ['', '', '', '', '', '', 'box (h)'], + nums: [ + '(upper fence: 8, Jan 2004)', + '(lower fence: 0, Jan 2004)', + '(max: 8, Jan 2004)', + '(min: 0, Jan 2004)', + '(q1: 2, Jan 2004)', + '(q3: 6, Jan 2004)', + '(median: 4, Jan 2004)' + ] + }); + }) - .then(done, done.fail); + .then(done, done.fail); }); - it('shows hover info and hovertemplate for bar and scatter traces using (start | middle | end) alignments and different periods', function(done) { + it('shows hover info and hovertemplate for bar and scatter traces using (start | middle | end) alignments and different periods', function (done) { Plotly.newPlot(gd, require('../../image/mocks/period_positioning4.json')) - .then(function() { _hover(65, 425); }) - .then(function() { - assertHoverLabelContent({ - name: 'start (M1)', - nums: '(Jan 1, 2001, 1)' - }); - }) - .then(function() { _hover(65, 395); }) - .then(function() { - assertHoverLabelContent({ - name: 'start (M1)', - nums: '(Jan 15, 2001, 2)' - }); - }) - .then(function() { _hover(100, 425); }) - .then(function() { - assertHoverLabelContent({ - name: 'middle (M1)', - nums: '(Jan 1, 2001, 1)' - }); - }) - .then(function() { _hover(100, 395); }) - .then(function() { - assertHoverLabelContent({ - name: 'middle (M1)', - nums: '(Jan 15, 2001, 2)' - }); - }) - .then(function() { _hover(135, 425); }) - .then(function() { - assertHoverLabelContent({ - name: 'end (M1)', - nums: '(Jan 1, 2001, 1)' - }); - }) - .then(function() { _hover(135, 395); }) - .then(function() { - assertHoverLabelContent({ - name: 'end (M1)', - nums: '(Jan 15, 2001, 2)' - }); - }) + .then(function () { + _hover(65, 425); + }) + .then(function () { + assertHoverLabelContent({ + name: 'start (M1)', + nums: '(Jan 1, 2001, 1)' + }); + }) + .then(function () { + _hover(65, 395); + }) + .then(function () { + assertHoverLabelContent({ + name: 'start (M1)', + nums: '(Jan 15, 2001, 2)' + }); + }) + .then(function () { + _hover(100, 425); + }) + .then(function () { + assertHoverLabelContent({ + name: 'middle (M1)', + nums: '(Jan 1, 2001, 1)' + }); + }) + .then(function () { + _hover(100, 395); + }) + .then(function () { + assertHoverLabelContent({ + name: 'middle (M1)', + nums: '(Jan 15, 2001, 2)' + }); + }) + .then(function () { + _hover(135, 425); + }) + .then(function () { + assertHoverLabelContent({ + name: 'end (M1)', + nums: '(Jan 1, 2001, 1)' + }); + }) + .then(function () { + _hover(135, 395); + }) + .then(function () { + assertHoverLabelContent({ + name: 'end (M1)', + nums: '(Jan 15, 2001, 2)' + }); + }) - .then(function() { _hover(65, 205); }) - .then(function() { - assertHoverLabelContent({ - name: 'start (M2)', - nums: '(Jan 1, 2001, 1)' - }); - }) - .then(function() { _hover(65, 175); }) - .then(function() { - assertHoverLabelContent({ - name: 'start (M2)', - nums: '(Feb 1, 2001, 2)' - }); - }) - .then(function() { _hover(100, 205); }) - .then(function() { - assertHoverLabelContent({ - name: 'middle (M2)', - nums: '(Jan 1, 2001, 1)' - }); - }) - .then(function() { _hover(100, 175); }) - .then(function() { - assertHoverLabelContent({ - name: 'middle (M2)', - nums: '(Feb 1, 2001, 2)' - }); - }) - .then(function() { _hover(135, 205); }) - .then(function() { - assertHoverLabelContent({ - name: 'end (M2)', - nums: '(Jan 1, 2001, 1)' - }); - }) - .then(function() { _hover(135, 175); }) - .then(function() { - assertHoverLabelContent({ - name: 'end (M2)', - nums: '(Feb 1, 2001, 2)' - }); - }) + .then(function () { + _hover(65, 205); + }) + .then(function () { + assertHoverLabelContent({ + name: 'start (M2)', + nums: '(Jan 1, 2001, 1)' + }); + }) + .then(function () { + _hover(65, 175); + }) + .then(function () { + assertHoverLabelContent({ + name: 'start (M2)', + nums: '(Feb 1, 2001, 2)' + }); + }) + .then(function () { + _hover(100, 205); + }) + .then(function () { + assertHoverLabelContent({ + name: 'middle (M2)', + nums: '(Jan 1, 2001, 1)' + }); + }) + .then(function () { + _hover(100, 175); + }) + .then(function () { + assertHoverLabelContent({ + name: 'middle (M2)', + nums: '(Feb 1, 2001, 2)' + }); + }) + .then(function () { + _hover(135, 205); + }) + .then(function () { + assertHoverLabelContent({ + name: 'end (M2)', + nums: '(Jan 1, 2001, 1)' + }); + }) + .then(function () { + _hover(135, 175); + }) + .then(function () { + assertHoverLabelContent({ + name: 'end (M2)', + nums: '(Feb 1, 2001, 2)' + }); + }) - .then(function() { _hover(345, 425); }) - .then(function() { - assertHoverLabelContent({ - name: 'start (M3)', - nums: '(Q1, 1)' - }); - }) - .then(function() { _hover(345, 395); }) - .then(function() { - assertHoverLabelContent({ - name: 'start (M3)', - nums: '(Q1, 2)' - }); - }) - .then(function() { _hover(380, 425); }) - .then(function() { - assertHoverLabelContent({ - name: 'middle (M3)', - nums: '(Q1, 1)' - }); - }) - .then(function() { _hover(415, 425); }) - .then(function() { - assertHoverLabelContent({ - name: 'end (M3)', - nums: '(Q1, 1)' - }); - }) - .then(function() { _hover(415, 395); }) - .then(function() { - assertHoverLabelContent({ - name: 'end (M3)', - nums: '(Q1, 2)' - }); - }) + .then(function () { + _hover(345, 425); + }) + .then(function () { + assertHoverLabelContent({ + name: 'start (M3)', + nums: '(Q1, 1)' + }); + }) + .then(function () { + _hover(345, 395); + }) + .then(function () { + assertHoverLabelContent({ + name: 'start (M3)', + nums: '(Q1, 2)' + }); + }) + .then(function () { + _hover(380, 425); + }) + .then(function () { + assertHoverLabelContent({ + name: 'middle (M3)', + nums: '(Q1, 1)' + }); + }) + .then(function () { + _hover(415, 425); + }) + .then(function () { + assertHoverLabelContent({ + name: 'end (M3)', + nums: '(Q1, 1)' + }); + }) + .then(function () { + _hover(415, 395); + }) + .then(function () { + assertHoverLabelContent({ + name: 'end (M3)', + nums: '(Q1, 2)' + }); + }) - .then(function() { _hover(630, 425); }) - .then(function() { - assertHoverLabelContent({ - name: 'start (M12)', - nums: '(Jan 2001, 1)' - }); - }) - .then(function() { _hover(630, 395); }) - .then(function() { - assertHoverLabelContent({ - name: 'start (M12)', - nums: '(Jul 2001, 2)' - }); - }) - .then(function() { _hover(665, 425); }) - .then(function() { - assertHoverLabelContent({ - name: 'middle (M12)', - nums: '(Jan 2001, 1)' - }); - }) - .then(function() { _hover(700, 425); }) - .then(function() { - assertHoverLabelContent({ - name: 'end (M12)', - nums: '(Jan 2001, 1)' - }); - }) - .then(function() { _hover(700, 395); }) - .then(function() { - assertHoverLabelContent({ - name: 'end (M12)', - nums: '(Jul 2001, 2)' - }); - }) + .then(function () { + _hover(630, 425); + }) + .then(function () { + assertHoverLabelContent({ + name: 'start (M12)', + nums: '(Jan 2001, 1)' + }); + }) + .then(function () { + _hover(630, 395); + }) + .then(function () { + assertHoverLabelContent({ + name: 'start (M12)', + nums: '(Jul 2001, 2)' + }); + }) + .then(function () { + _hover(665, 425); + }) + .then(function () { + assertHoverLabelContent({ + name: 'middle (M12)', + nums: '(Jan 2001, 1)' + }); + }) + .then(function () { + _hover(700, 425); + }) + .then(function () { + assertHoverLabelContent({ + name: 'end (M12)', + nums: '(Jan 2001, 1)' + }); + }) + .then(function () { + _hover(700, 395); + }) + .then(function () { + assertHoverLabelContent({ + name: 'end (M12)', + nums: '(Jul 2001, 2)' + }); + }) - .then(function() { _hover(630, 205); }) - .then(function() { - assertHoverLabelContent({ - name: 'start (W1)', - nums: '(W01, 1)' - }); - }) - .then(function() { _hover(630, 175); }) - .then(function() { - assertHoverLabelContent({ - name: 'start (W1)', - nums: '(W01, 2)' - }); - }) - .then(function() { _hover(665, 205); }) - .then(function() { - assertHoverLabelContent({ - name: 'middle (W1)', - nums: 'Monday, 1' - }); - }) - .then(function() { _hover(665, 175); }) - .then(function() { - assertHoverLabelContent({ - name: 'middle (W1)', - nums: 'Friday, 2' - }); - }) - .then(function() { _hover(700, 205); }) - .then(function() { - assertHoverLabelContent({ - name: 'end (W1)', - nums: '(W01, 1)' - }); - }) - .then(function() { _hover(700, 175); }) - .then(function() { - assertHoverLabelContent({ - name: 'end (W1)', - nums: '(W01, 2)' - }); - }) + .then(function () { + _hover(630, 205); + }) + .then(function () { + assertHoverLabelContent({ + name: 'start (W1)', + nums: '(W01, 1)' + }); + }) + .then(function () { + _hover(630, 175); + }) + .then(function () { + assertHoverLabelContent({ + name: 'start (W1)', + nums: '(W01, 2)' + }); + }) + .then(function () { + _hover(665, 205); + }) + .then(function () { + assertHoverLabelContent({ + name: 'middle (W1)', + nums: 'Monday, 1' + }); + }) + .then(function () { + _hover(665, 175); + }) + .then(function () { + assertHoverLabelContent({ + name: 'middle (W1)', + nums: 'Friday, 2' + }); + }) + .then(function () { + _hover(700, 205); + }) + .then(function () { + assertHoverLabelContent({ + name: 'end (W1)', + nums: '(W01, 1)' + }); + }) + .then(function () { + _hover(700, 175); + }) + .then(function () { + assertHoverLabelContent({ + name: 'end (W1)', + nums: '(W01, 2)' + }); + }) + + .then(done, done.fail); + }); +}); + +describe('Hover on axes with rangebreaks', function () { + var gd; + var eventData; + + beforeEach(function () { + gd = createGraphDiv(); + }); + + afterEach(destroyGraphDiv); + + function _hover(x, y) { + delete gd._hoverdata; + Lib.clearThrottle(); + mouseEvent('mousemove', x, y); + } + + function _assert(msg, exp, noHoverLabel) { + if (!noHoverLabel) assertHoverLabelContent({ nums: exp.nums, axis: exp.axis }, msg + '| hover label'); + expect(eventData.x).toBe(exp.x, 'event data x'); + expect(eventData.y).toBe(exp.y, 'event data y'); + } + + it('should work when rangebreaks are present on x-axis', function (done) { + Plotly.newPlot( + gd, + [ + { + mode: 'lines', // i.e. no autorange padding + x: [ + '1970-01-01 00:00:00.000', + '1970-01-01 00:00:00.010', + '1970-01-01 00:00:00.050', + '1970-01-01 00:00:00.090', + '1970-01-01 00:00:00.095', + '1970-01-01 00:00:00.100', + '1970-01-01 00:00:00.150', + '1970-01-01 00:00:00.190', + '1970-01-01 00:00:00.200' + ] + } + ], + { + xaxis: { + rangebreaks: [ + { bounds: ['1970-01-01 00:00:00.011', '1970-01-01 00:00:00.089'] }, + { bounds: ['1970-01-01 00:00:00.101', '1970-01-01 00:00:00.189'] } + ] + }, + width: 400, + height: 400, + margin: { l: 10, t: 10, b: 10, r: 10 }, + hovermode: 'x' + } + ) + .then(function () { + gd.on('plotly_hover', function (d) { + eventData = d.points[0]; + }); + }) + .then(function () { + _hover(11, 11); + }) + .then(function () { + _assert('leftmost interval', { + nums: '0', + axis: 'Jan 1, 1970', + x: '1970-01-01', + y: 0 + }); + }) + .then(function () { + _hover(200, 200); + }) + .then(function () { + _assert('middle interval', { + nums: '4', + axis: 'Jan 1, 1970, 00:00:00.095', + x: '1970-01-01 00:00:00.095', + y: 4 + }); + }) + .then(function () { + _hover(388, 388); + }) + .then(function () { + _assert('rightmost interval', { + nums: '8', + axis: 'Jan 1, 1970, 00:00:00.2', + x: '1970-01-01 00:00:00.2', + y: 8 + }); + }) + .then(done, done.fail); + }); + + it('should work when rangebreaks are present on x-axis (reversed range)', function (done) { + Plotly.newPlot( + gd, + [ + { + mode: 'lines', // i.e. no autorange padding + x: [ + '1970-01-01 00:00:00.000', + '1970-01-01 00:00:00.010', + '1970-01-01 00:00:00.050', + '1970-01-01 00:00:00.090', + '1970-01-01 00:00:00.095', + '1970-01-01 00:00:00.100', + '1970-01-01 00:00:00.150', + '1970-01-01 00:00:00.190', + '1970-01-01 00:00:00.200' + ] + } + ], + { + xaxis: { + autorange: 'reversed', + rangebreaks: [ + { bounds: ['1970-01-01 00:00:00.011', '1970-01-01 00:00:00.089'] }, + { bounds: ['1970-01-01 00:00:00.101', '1970-01-01 00:00:00.189'] } + ] + }, + width: 400, + height: 400, + margin: { l: 10, t: 10, b: 10, r: 10 }, + hovermode: 'x' + } + ) + .then(function () { + gd.on('plotly_hover', function (d) { + eventData = d.points[0]; + }); + }) + .then(function () { + _hover(11, 11); + }) + .then(function () { + _assert('leftmost interval', { + nums: '8', + axis: 'Jan 1, 1970, 00:00:00.2', + x: '1970-01-01 00:00:00.2', + y: 8 + }); + }) + .then(function () { + _hover(200, 200); + }) + .then(function () { + _assert('middle interval', { + nums: '4', + axis: 'Jan 1, 1970, 00:00:00.095', + x: '1970-01-01 00:00:00.095', + y: 4 + }); + }) + .then(function () { + _hover(388, 388); + }) + .then(function () { + _assert('rightmost interval', { + nums: '0', + axis: 'Jan 1, 1970', + x: '1970-01-01', + y: 0 + }); + }) + .then(done, done.fail); + }); - .then(done, done.fail); + it('should work when rangebreaks are present on y-axis using hovermode x (case of bar and autorange reversed)', function (done) { + Plotly.newPlot( + gd, + [ + { + type: 'bar', + orientation: 'h', + y: [ + '1970-01-01 00:00:00.000', + '1970-01-01 00:00:00.010', + '1970-01-01 00:00:00.050', + '1970-01-01 00:00:00.090', + '1970-01-01 00:00:00.095', + '1970-01-01 00:00:00.100', + '1970-01-01 00:00:00.150', + '1970-01-01 00:00:00.190', + '1970-01-01 00:00:00.200' + ], + x: [0, 1, 2, 3, 4, 5, 6, 7, 8] + } + ], + { + yaxis: { + autorange: 'reversed', + rangebreaks: [ + { bounds: ['1970-01-01 00:00:00.011', '1970-01-01 00:00:00.089'] }, + { bounds: ['1970-01-01 00:00:00.101', '1970-01-01 00:00:00.189'] } + ] + }, + width: 400, + height: 400, + margin: { l: 10, t: 10, b: 10, r: 10 }, + hovermode: 'x' + } + ) + .then(function () { + gd.on('plotly_hover', function (d) { + eventData = d.points[0]; + }); + }) + .then(function () { + _hover(50, 350); + }) + .then(function () { + _assert('1st test', { + axis: '1', + nums: 'Jan 1, 1970, 00:00:00.01', + y: '1970-01-01 00:00:00.01', + x: 1 + }); + }) + .then(function () { + _hover(200, 200); + }) + .then(function () { + _assert('2nd test', { + axis: '5', + nums: 'Jan 1, 1970, 00:00:00.1', + y: '1970-01-01 00:00:00.1', + x: 5 + }); + }) + .then(function () { + _hover(250, 150); + }) // hover over break + .then(function () { + _assert( + '3rd test', + { + // previous values + y: '1970-01-01 00:00:00.1', + x: 5 + }, + 'noHoverLabel' + ); + }) + .then(function () { + _hover(350, 50); + }) + .then(function () { + _assert('4th test', { + axis: '8', + nums: 'Jan 1, 1970, 00:00:00.2', + y: '1970-01-01 00:00:00.2', + x: 8 + }); + }) + .then(done, done.fail); }); -}); - -describe('Hover on axes with rangebreaks', function() { - var gd; - var eventData; - beforeEach(function() { - gd = createGraphDiv(); + it('should work when rangebreaks are present on y-axis', function (done) { + Plotly.newPlot( + gd, + [ + { + mode: 'lines', // i.e. no autorange padding + y: [ + '1970-01-01 00:00:00.000', + '1970-01-01 00:00:00.010', + '1970-01-01 00:00:00.050', + '1970-01-01 00:00:00.090', + '1970-01-01 00:00:00.095', + '1970-01-01 00:00:00.100', + '1970-01-01 00:00:00.150', + '1970-01-01 00:00:00.190', + '1970-01-01 00:00:00.200' + ] + } + ], + { + yaxis: { + rangebreaks: [ + { bounds: ['1970-01-01 00:00:00.011', '1970-01-01 00:00:00.089'] }, + { bounds: ['1970-01-01 00:00:00.101', '1970-01-01 00:00:00.189'] } + ] + }, + width: 400, + height: 400, + margin: { l: 10, t: 10, b: 10, r: 10 }, + hovermode: 'y' + } + ) + .then(function () { + gd.on('plotly_hover', function (d) { + eventData = d.points[0]; + }); + }) + .then(function () { + _hover(388, 30); + }) + .then(function () { + _assert('topmost interval', { + nums: '8', + axis: 'Jan 1, 1970, 00:00:00.2', + x: 8, + y: '1970-01-01 00:00:00.2' + }); + }) + .then(function () { + _hover(200, 200); + }) + .then(function () { + _assert('middle interval', { + nums: '4', + axis: 'Jan 1, 1970, 00:00:00.095', + x: 4, + y: '1970-01-01 00:00:00.095' + }); + }) + .then(function () { + _hover(11, 370); + }) + .then(function () { + _assert('bottom interval', { + nums: '0', + axis: 'Jan 1, 1970', + x: 0, + y: '1970-01-01' + }); + }) + .then(done, done.fail); }); - afterEach(destroyGraphDiv); - - function _hover(x, y) { - delete gd._hoverdata; - Lib.clearThrottle(); - mouseEvent('mousemove', x, y); - } - - function _assert(msg, exp, noHoverLabel) { - if(!noHoverLabel) assertHoverLabelContent({ nums: exp.nums, axis: exp.axis }, msg + '| hover label'); - expect(eventData.x).toBe(exp.x, 'event data x'); - expect(eventData.y).toBe(exp.y, 'event data y'); - } - - it('should work when rangebreaks are present on x-axis', function(done) { - Plotly.newPlot(gd, [{ - mode: 'lines', // i.e. no autorange padding - x: [ - '1970-01-01 00:00:00.000', - '1970-01-01 00:00:00.010', - '1970-01-01 00:00:00.050', - '1970-01-01 00:00:00.090', - '1970-01-01 00:00:00.095', - '1970-01-01 00:00:00.100', - '1970-01-01 00:00:00.150', - '1970-01-01 00:00:00.190', - '1970-01-01 00:00:00.200' - ] - }], { - xaxis: { - rangebreaks: [ - {bounds: [ - '1970-01-01 00:00:00.011', - '1970-01-01 00:00:00.089' - ]}, - {bounds: [ - '1970-01-01 00:00:00.101', - '1970-01-01 00:00:00.189' - ]} - ] - }, - width: 400, - height: 400, - margin: {l: 10, t: 10, b: 10, r: 10}, - hovermode: 'x' - }) - .then(function() { - gd.on('plotly_hover', function(d) { - eventData = d.points[0]; - }); - }) - .then(function() { _hover(11, 11); }) - .then(function() { - _assert('leftmost interval', { - nums: '0', - axis: 'Jan 1, 1970', - x: '1970-01-01', - y: 0 - }); - }) - .then(function() { _hover(200, 200); }) - .then(function() { - _assert('middle interval', { - nums: '4', - axis: 'Jan 1, 1970, 00:00:00.095', - x: '1970-01-01 00:00:00.095', - y: 4 - }); - }) - .then(function() { _hover(388, 388); }) - .then(function() { - _assert('rightmost interval', { - nums: '8', - axis: 'Jan 1, 1970, 00:00:00.2', - x: '1970-01-01 00:00:00.2', - y: 8 - }); - }) - .then(done, done.fail); - }); - - it('should work when rangebreaks are present on x-axis (reversed range)', function(done) { - Plotly.newPlot(gd, [{ - mode: 'lines', // i.e. no autorange padding - x: [ - '1970-01-01 00:00:00.000', - '1970-01-01 00:00:00.010', - '1970-01-01 00:00:00.050', - '1970-01-01 00:00:00.090', - '1970-01-01 00:00:00.095', - '1970-01-01 00:00:00.100', - '1970-01-01 00:00:00.150', - '1970-01-01 00:00:00.190', - '1970-01-01 00:00:00.200' - ] - }], { - xaxis: { - autorange: 'reversed', - rangebreaks: [ - {bounds: [ - '1970-01-01 00:00:00.011', - '1970-01-01 00:00:00.089' - ]}, - {bounds: [ - '1970-01-01 00:00:00.101', - '1970-01-01 00:00:00.189' - ]} - ] - }, - width: 400, - height: 400, - margin: {l: 10, t: 10, b: 10, r: 10}, - hovermode: 'x' - }) - .then(function() { - gd.on('plotly_hover', function(d) { - eventData = d.points[0]; - }); - }) - .then(function() { _hover(11, 11); }) - .then(function() { - _assert('leftmost interval', { - nums: '8', - axis: 'Jan 1, 1970, 00:00:00.2', - x: '1970-01-01 00:00:00.2', - y: 8 - }); - }) - .then(function() { _hover(200, 200); }) - .then(function() { - _assert('middle interval', { - nums: '4', - axis: 'Jan 1, 1970, 00:00:00.095', - x: '1970-01-01 00:00:00.095', - y: 4 - }); - }) - .then(function() { _hover(388, 388); }) - .then(function() { - _assert('rightmost interval', { - nums: '0', - axis: 'Jan 1, 1970', - x: '1970-01-01', - y: 0 - }); - }) - .then(done, done.fail); - }); - - it('should work when rangebreaks are present on y-axis using hovermode x (case of bar and autorange reversed)', function(done) { - Plotly.newPlot(gd, [{ - type: 'bar', - orientation: 'h', - y: [ - '1970-01-01 00:00:00.000', - '1970-01-01 00:00:00.010', - '1970-01-01 00:00:00.050', - '1970-01-01 00:00:00.090', - '1970-01-01 00:00:00.095', - '1970-01-01 00:00:00.100', - '1970-01-01 00:00:00.150', - '1970-01-01 00:00:00.190', - '1970-01-01 00:00:00.200' + it('should work when rangebreaks are present on y-axis (reversed range)', function (done) { + Plotly.newPlot( + gd, + [ + { + mode: 'lines', // i.e. no autorange padding + y: [ + '1970-01-01 00:00:00.000', + '1970-01-01 00:00:00.010', + '1970-01-01 00:00:00.050', + '1970-01-01 00:00:00.090', + '1970-01-01 00:00:00.095', + '1970-01-01 00:00:00.100', + '1970-01-01 00:00:00.150', + '1970-01-01 00:00:00.190', + '1970-01-01 00:00:00.200' + ] + } ], - x: [0, 1, 2, 3, 4, 5, 6, 7, 8] - }], { - yaxis: { - autorange: 'reversed', - rangebreaks: [ - {bounds: [ - '1970-01-01 00:00:00.011', - '1970-01-01 00:00:00.089' - ]}, - {bounds: [ - '1970-01-01 00:00:00.101', - '1970-01-01 00:00:00.189' - ]} - ] - }, - width: 400, - height: 400, - margin: {l: 10, t: 10, b: 10, r: 10}, - hovermode: 'x' - }) - .then(function() { - gd.on('plotly_hover', function(d) { - eventData = d.points[0]; - }); - }) - .then(function() { _hover(50, 350); }) - .then(function() { - _assert('1st test', { - axis: '1', - nums: 'Jan 1, 1970, 00:00:00.01', - y: '1970-01-01 00:00:00.01', - x: 1 - }); - }) - .then(function() { _hover(200, 200); }) - .then(function() { - _assert('2nd test', { - axis: '5', - nums: 'Jan 1, 1970, 00:00:00.1', - y: '1970-01-01 00:00:00.1', - x: 5 - }); - }) - .then(function() { _hover(250, 150); }) // hover over break - .then(function() { - _assert('3rd test', { - // previous values - y: '1970-01-01 00:00:00.1', - x: 5 - }, 'noHoverLabel'); - }) - .then(function() { _hover(350, 50); }) - .then(function() { - _assert('4th test', { - axis: '8', - nums: 'Jan 1, 1970, 00:00:00.2', - y: '1970-01-01 00:00:00.2', - x: 8 - }); - }) - .then(done, done.fail); - }); - - it('should work when rangebreaks are present on y-axis', function(done) { - Plotly.newPlot(gd, [{ - mode: 'lines', // i.e. no autorange padding - y: [ - '1970-01-01 00:00:00.000', - '1970-01-01 00:00:00.010', - '1970-01-01 00:00:00.050', - '1970-01-01 00:00:00.090', - '1970-01-01 00:00:00.095', - '1970-01-01 00:00:00.100', - '1970-01-01 00:00:00.150', - '1970-01-01 00:00:00.190', - '1970-01-01 00:00:00.200' - ] - }], { - yaxis: { - rangebreaks: [ - {bounds: [ - '1970-01-01 00:00:00.011', - '1970-01-01 00:00:00.089' - ]}, - {bounds: [ - '1970-01-01 00:00:00.101', - '1970-01-01 00:00:00.189' - ]} - ] - }, - width: 400, - height: 400, - margin: {l: 10, t: 10, b: 10, r: 10}, - hovermode: 'y' - }) - .then(function() { - gd.on('plotly_hover', function(d) { - eventData = d.points[0]; - }); - }) - .then(function() { _hover(388, 30); }) - .then(function() { - _assert('topmost interval', { - nums: '8', - axis: 'Jan 1, 1970, 00:00:00.2', - x: 8, - y: '1970-01-01 00:00:00.2' - }); - }) - .then(function() { _hover(200, 200); }) - .then(function() { - _assert('middle interval', { - nums: '4', - axis: 'Jan 1, 1970, 00:00:00.095', - x: 4, - y: '1970-01-01 00:00:00.095' - }); - }) - .then(function() { _hover(11, 370); }) - .then(function() { - _assert('bottom interval', { - nums: '0', - axis: 'Jan 1, 1970', - x: 0, - y: '1970-01-01' - }); - }) - .then(done, done.fail); - }); - - it('should work when rangebreaks are present on y-axis (reversed range)', function(done) { - Plotly.newPlot(gd, [{ - mode: 'lines', // i.e. no autorange padding - y: [ - '1970-01-01 00:00:00.000', - '1970-01-01 00:00:00.010', - '1970-01-01 00:00:00.050', - '1970-01-01 00:00:00.090', - '1970-01-01 00:00:00.095', - '1970-01-01 00:00:00.100', - '1970-01-01 00:00:00.150', - '1970-01-01 00:00:00.190', - '1970-01-01 00:00:00.200' - ] - }], { - yaxis: { - autorange: 'reversed', - rangebreaks: [ - {bounds: [ - '1970-01-01 00:00:00.011', - '1970-01-01 00:00:00.089' - ]}, - {bounds: [ - '1970-01-01 00:00:00.101', - '1970-01-01 00:00:00.189' - ]} - ] - }, - width: 400, - height: 400, - margin: {l: 10, t: 10, b: 10, r: 10}, - hovermode: 'y' - }) - .then(function() { - gd.on('plotly_hover', function(d) { - eventData = d.points[0]; - }); - }) - .then(function() { _hover(388, 30); }) - .then(function() { - _assert('topmost interval', { - nums: '0', - axis: 'Jan 1, 1970', - x: 0, - y: '1970-01-01' - }); - }) - .then(function() { _hover(200, 200); }) - .then(function() { - _assert('middle interval', { - nums: '4', - axis: 'Jan 1, 1970, 00:00:00.095', - x: 4, - y: '1970-01-01 00:00:00.095' - }); - }) - .then(function() { _hover(11, 370); }) - .then(function() { - _assert('bottom interval', { - nums: '8', - axis: 'Jan 1, 1970, 00:00:00.2', - x: 8, - y: '1970-01-01 00:00:00.2' - }); - }) - .then(done, done.fail); + { + yaxis: { + autorange: 'reversed', + rangebreaks: [ + { bounds: ['1970-01-01 00:00:00.011', '1970-01-01 00:00:00.089'] }, + { bounds: ['1970-01-01 00:00:00.101', '1970-01-01 00:00:00.189'] } + ] + }, + width: 400, + height: 400, + margin: { l: 10, t: 10, b: 10, r: 10 }, + hovermode: 'y' + } + ) + .then(function () { + gd.on('plotly_hover', function (d) { + eventData = d.points[0]; + }); + }) + .then(function () { + _hover(388, 30); + }) + .then(function () { + _assert('topmost interval', { + nums: '0', + axis: 'Jan 1, 1970', + x: 0, + y: '1970-01-01' + }); + }) + .then(function () { + _hover(200, 200); + }) + .then(function () { + _assert('middle interval', { + nums: '4', + axis: 'Jan 1, 1970, 00:00:00.095', + x: 4, + y: '1970-01-01 00:00:00.095' + }); + }) + .then(function () { + _hover(11, 370); + }) + .then(function () { + _assert('bottom interval', { + nums: '8', + axis: 'Jan 1, 1970, 00:00:00.2', + x: 8, + y: '1970-01-01 00:00:00.2' + }); + }) + .then(done, done.fail); }); }); -describe('hover updates', function() { +describe('hover updates', function () { 'use strict'; afterEach(destroyGraphDiv); @@ -4132,12 +4583,12 @@ describe('hover updates', function() { function assertLabelsCorrect(mousePos, labelPos, labelText, msg) { Lib.clearThrottle(); - if(mousePos) { + if (mousePos) { mouseEvent('mousemove', mousePos[0], mousePos[1]); } var hoverText = d3SelectAll('g.hovertext'); - if(labelPos) { + if (labelPos) { expect(hoverText.size()).toBe(1, msg); expect(hoverText.text()).toBe(labelText, msg); @@ -4151,54 +4602,67 @@ describe('hover updates', function() { } } - it('should update the labels on animation', function(done) { + it('should update the labels on animation', function (done) { var mock = { data: [ - {x: [0.5], y: [0.5], showlegend: false}, - {x: [0], y: [0], showlegend: false}, + { x: [0.5], y: [0.5], showlegend: false }, + { x: [0], y: [0], showlegend: false } ], layout: { hovermode: 'x', - margin: {t: 0, r: 0, b: 0, l: 0}, + margin: { t: 0, r: 0, b: 0, l: 0 }, width: 200, height: 200, - xaxis: {range: [0, 1]}, - yaxis: {range: [0, 1]}, + xaxis: { range: [0, 1] }, + yaxis: { range: [0, 1] } } }; var gd = createGraphDiv(); - Plotly.newPlot(gd, mock).then(function() { - // The label text gets concatenated together when queried. Such is life. - assertLabelsCorrect([100, 100], [103, 100], 'trace 00.5', 'animation/update 0'); - }).then(function() { - return Plotly.animate(gd, [{ - data: [{x: [0], y: [0]}, {x: [0.5], y: [0.5]}], - traces: [0, 1], - }], {frame: {redraw: false, duration: 0}}); - }) - .then(delay(HOVERMINTIME)) - .then(function() { - // No mouse event this time. Just change the data and check the label. - // Ditto on concatenation. This is "trace 1" + "0.5" - assertLabelsCorrect(null, [103, 100], 'trace 10.5', 'animation/update 1'); - - // Restyle to move the point out of the window: - return Plotly.relayout(gd, {'xaxis.range': [2, 3]}); - }).then(function() { - // Assert label removed: - assertLabelsCorrect(null, null, null, 'animation/update 2'); - - // Move back to the original xaxis range: - return Plotly.relayout(gd, {'xaxis.range': [0, 1]}); - }).then(function() { - // Assert label restored: - assertLabelsCorrect(null, [103, 100], 'trace 10.5', 'animation/update 3'); - }) - .then(done, done.fail); + Plotly.newPlot(gd, mock) + .then(function () { + // The label text gets concatenated together when queried. Such is life. + assertLabelsCorrect([100, 100], [103, 100], 'trace 00.5', 'animation/update 0'); + }) + .then(function () { + return Plotly.animate( + gd, + [ + { + data: [ + { x: [0], y: [0] }, + { x: [0.5], y: [0.5] } + ], + traces: [0, 1] + } + ], + { frame: { redraw: false, duration: 0 } } + ); + }) + .then(delay(HOVERMINTIME)) + .then(function () { + // No mouse event this time. Just change the data and check the label. + // Ditto on concatenation. This is "trace 1" + "0.5" + assertLabelsCorrect(null, [103, 100], 'trace 10.5', 'animation/update 1'); + + // Restyle to move the point out of the window: + return Plotly.relayout(gd, { 'xaxis.range': [2, 3] }); + }) + .then(function () { + // Assert label removed: + assertLabelsCorrect(null, null, null, 'animation/update 2'); + + // Move back to the original xaxis range: + return Plotly.relayout(gd, { 'xaxis.range': [0, 1] }); + }) + .then(function () { + // Assert label restored: + assertLabelsCorrect(null, [103, 100], 'trace 10.5', 'animation/update 3'); + }) + .then(done, done.fail); }); - it('should not trigger infinite loop of plotly_unhover events', function(done) { + it('should not trigger infinite loop of plotly_unhover events', function (done) { var gd = createGraphDiv(); var colors0 = ['#000000', '#000000', '#000000', '#000000', '#000000', '#000000', '#000000']; @@ -4210,64 +4674,68 @@ describe('hover updates', function() { var hoverCnt = 0; var unHoverCnt = 0; - Plotly.newPlot(gd, [{ - mode: 'markers', - x: [1, 2, 3, 4, 5, 6, 7], - y: [1, 2, 3, 2, 3, 4, 3], - marker: { - size: 16, - color: colors0.slice() - } - }], { width: 700, height: 450, hovermode: 'x' }) - .then(function() { - gd.on('plotly_hover', function(eventData) { - hoverCnt++; - - var pt = eventData.points[0]; - Plotly.restyle(gd, 'marker.color[' + pt.pointNumber + ']', 'red'); - }); + Plotly.newPlot( + gd, + [ + { + mode: 'markers', + x: [1, 2, 3, 4, 5, 6, 7], + y: [1, 2, 3, 2, 3, 4, 3], + marker: { + size: 16, + color: colors0.slice() + } + } + ], + { width: 700, height: 450, hovermode: 'x' } + ) + .then(function () { + gd.on('plotly_hover', function (eventData) { + hoverCnt++; + + var pt = eventData.points[0]; + Plotly.restyle(gd, 'marker.color[' + pt.pointNumber + ']', 'red'); + }); - gd.on('plotly_unhover', function() { - unHoverCnt++; + gd.on('plotly_unhover', function () { + unHoverCnt++; - Plotly.restyle(gd, 'marker.color', [colors0.slice()]); - }); + Plotly.restyle(gd, 'marker.color', [colors0.slice()]); + }); - assertLabelsCorrect([351, 251], [358, 272], '2', 'events 0'); + assertLabelsCorrect([351, 251], [358, 272], '2', 'events 0'); - unhover(); - expect(hoverCnt).toEqual(1); - expect(unHoverCnt).toEqual(1); + unhover(); + expect(hoverCnt).toEqual(1); + expect(unHoverCnt).toEqual(1); - assertLabelsCorrect([420, 100], [435, 198], '3', 'events 1'); + assertLabelsCorrect([420, 100], [435, 198], '3', 'events 1'); - unhover(); - expect(hoverCnt).toEqual(2); - expect(unHoverCnt).toEqual(2); - }) - .then(done); + unhover(); + expect(hoverCnt).toEqual(2); + expect(unHoverCnt).toEqual(2); + }) + .then(done); }); - it('should not attempt to rehover over exiting subplots', function(done) { + it('should not attempt to rehover over exiting subplots', function (done) { spyOn(Fx, 'hover').and.callThrough(); var data = [ - {y: [1], hoverinfo: 'y'}, - {y: [2], hoverinfo: 'y', xaxis: 'x2', yaxis: 'y2'} + { y: [1], hoverinfo: 'y' }, + { y: [2], hoverinfo: 'y', xaxis: 'x2', yaxis: 'y2' } ]; - var data2 = [ - {y: [1], hoverinfo: 'y'} - ]; + var data2 = [{ y: [1], hoverinfo: 'y' }]; var layout = { hovermode: 'x', - xaxis: {domain: [0, 0.5]}, - xaxis2: {anchor: 'y2', domain: [0.5, 1]}, - yaxis2: {anchor: 'x2'}, + xaxis: { domain: [0, 0.5] }, + xaxis2: { anchor: 'y2', domain: [0.5, 1] }, + yaxis2: { anchor: 'x2' }, width: 400, height: 200, - margin: {l: 0, t: 0, r: 0, b: 0}, + margin: { l: 0, t: 0, r: 0, b: 0 }, showlegend: false }; @@ -4276,36 +4744,36 @@ describe('hover updates', function() { var offPt2 = [250, 100]; Plotly.react(gd, data, layout) - .then(function() { - assertLabelsCorrect(onPt2, [303, 100], '2', 'after 1st draw [on-pt]'); - assertLabelsCorrect(offPt2, null, null, 'after 1st draw [off-pt]'); - expect(Fx.hover).toHaveBeenCalledTimes(2); - }) - .then(function() { - var promise = Plotly.react(gd, data2, layout); - assertLabelsCorrect(onPt2, null, null, '2', 'before react() resolves [on-pt]'); - assertLabelsCorrect(offPt2, null, null, 'before react() resolves [off-pt]'); - // N.B. no calls from Plots.rehover() as x2y2 subplot got removed! - expect(Fx.hover).toHaveBeenCalledTimes(2); - return promise; - }) - .then(function() { - expect(Fx.hover).toHaveBeenCalledTimes(2); - assertLabelsCorrect(onPt2, null, null, '2', 'after react() resolves [on-pt]'); - assertLabelsCorrect(offPt2, null, null, 'after react() resolves [off-pt]'); - expect(Fx.hover).toHaveBeenCalledTimes(2); - }) - .then(done, done.fail); + .then(function () { + assertLabelsCorrect(onPt2, [303, 100], '2', 'after 1st draw [on-pt]'); + assertLabelsCorrect(offPt2, null, null, 'after 1st draw [off-pt]'); + expect(Fx.hover).toHaveBeenCalledTimes(2); + }) + .then(function () { + var promise = Plotly.react(gd, data2, layout); + assertLabelsCorrect(onPt2, null, null, '2', 'before react() resolves [on-pt]'); + assertLabelsCorrect(offPt2, null, null, 'before react() resolves [off-pt]'); + // N.B. no calls from Plots.rehover() as x2y2 subplot got removed! + expect(Fx.hover).toHaveBeenCalledTimes(2); + return promise; + }) + .then(function () { + expect(Fx.hover).toHaveBeenCalledTimes(2); + assertLabelsCorrect(onPt2, null, null, '2', 'after react() resolves [on-pt]'); + assertLabelsCorrect(offPt2, null, null, 'after react() resolves [off-pt]'); + expect(Fx.hover).toHaveBeenCalledTimes(2); + }) + .then(done, done.fail); }); - it('drag should trigger unhover', function(done) { - var data = [{y: [1]}]; + it('drag should trigger unhover', function (done) { + var data = [{ y: [1] }]; var layout = { hovermode: 'x', width: 400, height: 200, - margin: {l: 0, t: 0, r: 0, b: 0}, + margin: { l: 0, t: 0, r: 0, b: 0 }, showlegend: false }; @@ -4330,17 +4798,17 @@ describe('hover updates', function() { } Plotly.react(gd, data, layout) - .then(function() { + .then(function () { gd.on('plotly_hover', hoverHandler); gd.on('plotly_unhover', unhoverHandler); }) .then(hover) - .then(function() { + .then(function () { expect(hoverHandler).toHaveBeenCalled(); expect(unhoverHandler).not.toHaveBeenCalled(); }) .then(drag) - .then(function() { + .then(function () { expect(hoverHandler).toHaveBeenCalled(); expect(unhoverHandler).toHaveBeenCalled(); }) @@ -4348,24 +4816,27 @@ describe('hover updates', function() { }); }); -describe('Test hover label custom styling:', function() { +describe('Test hover label custom styling:', function () { afterEach(destroyGraphDiv); function assertLabel(className, expectation) { var g = d3Select('g.' + className); - if(expectation === null) { + if (expectation === null) { expect(g.size()).toBe(0); } else { - assertHoverLabelStyle(g, { - bgcolor: expectation.path[0], - bordercolor: expectation.path[1], - fontSize: expectation.text[0], - fontFamily: expectation.text[1], - fontColor: expectation.text[2] - }, - '', - {hovertext: 'text.nums', axistext: 'text'}[className]); + assertHoverLabelStyle( + g, + { + bgcolor: expectation.path[0], + bordercolor: expectation.path[1], + fontSize: expectation.text[0], + fontFamily: expectation.text[1], + fontColor: expectation.text[2] + }, + '', + { hovertext: 'text.nums', axistext: 'text' }[className] + ); } } @@ -4382,314 +4853,344 @@ describe('Test hover label custom styling:', function() { Lib.clearThrottle(); } - it('should work for x/y cartesian traces', function(done) { + it('should work for x/y cartesian traces', function (done) { var gd = createGraphDiv(); - Plotly.newPlot(gd, [{ - x: [1, 2, 3], - y: [1, 2, 1], - marker: { - color: ['yellow', 'black', 'cyan'] - }, - hoverlabel: { - font: { - color: ['red', 'green', 'blue'], - size: 20 + Plotly.newPlot( + gd, + [ + { + x: [1, 2, 3], + y: [1, 2, 1], + marker: { + color: ['yellow', 'black', 'cyan'] + }, + hoverlabel: { + font: { + color: ['red', 'green', 'blue'], + size: 20 + } + } } + ], + { + hovermode: 'x', + hoverlabel: { bgcolor: 'white' } } - }], { - hovermode: 'x', - hoverlabel: { bgcolor: 'white' } - }) - .then(function() { - _hover(gd, { xval: gd._fullData[0].x[0] }); + ) + .then(function () { + _hover(gd, { xval: gd._fullData[0].x[0] }); - assertPtLabel({ - path: ['rgb(255, 255, 255)', 'rgb(68, 68, 68)'], - text: [20, 'Arial', 'rgb(255, 0, 0)'] - }); - assertCommonLabel({ - path: ['rgb(255, 255, 255)', 'rgb(68, 68, 68)'], - text: [13, 'Arial', 'rgb(68, 68, 68)'] - }); - }) - .then(function() { - _hover(gd, { xval: gd._fullData[0].x[1] }); + assertPtLabel({ + path: ['rgb(255, 255, 255)', 'rgb(68, 68, 68)'], + text: [20, 'Arial', 'rgb(255, 0, 0)'] + }); + assertCommonLabel({ + path: ['rgb(255, 255, 255)', 'rgb(68, 68, 68)'], + text: [13, 'Arial', 'rgb(68, 68, 68)'] + }); + }) + .then(function () { + _hover(gd, { xval: gd._fullData[0].x[1] }); - assertPtLabel({ - path: ['rgb(255, 255, 255)', 'rgb(68, 68, 68)'], - text: [20, 'Arial', 'rgb(0, 128, 0)'] - }); - assertCommonLabel({ - path: ['rgb(255, 255, 255)', 'rgb(68, 68, 68)'], - text: [13, 'Arial', 'rgb(68, 68, 68)'] - }); - }) - .then(function() { - _hover(gd, { xval: gd._fullData[0].x[2] }); + assertPtLabel({ + path: ['rgb(255, 255, 255)', 'rgb(68, 68, 68)'], + text: [20, 'Arial', 'rgb(0, 128, 0)'] + }); + assertCommonLabel({ + path: ['rgb(255, 255, 255)', 'rgb(68, 68, 68)'], + text: [13, 'Arial', 'rgb(68, 68, 68)'] + }); + }) + .then(function () { + _hover(gd, { xval: gd._fullData[0].x[2] }); - assertPtLabel({ - path: ['rgb(255, 255, 255)', 'rgb(68, 68, 68)'], - text: [20, 'Arial', 'rgb(0, 0, 255)'] - }); - assertCommonLabel({ - path: ['rgb(255, 255, 255)', 'rgb(68, 68, 68)'], - text: [13, 'Arial', 'rgb(68, 68, 68)'] - }); + assertPtLabel({ + path: ['rgb(255, 255, 255)', 'rgb(68, 68, 68)'], + text: [20, 'Arial', 'rgb(0, 0, 255)'] + }); + assertCommonLabel({ + path: ['rgb(255, 255, 255)', 'rgb(68, 68, 68)'], + text: [13, 'Arial', 'rgb(68, 68, 68)'] + }); - // test arrayOk case - return Plotly.restyle(gd, 'hoverinfo', [['skip', 'name', 'x']]); - }) - .then(function() { - _hover(gd, { xval: gd._fullData[0].x[0] }); + // test arrayOk case + return Plotly.restyle(gd, 'hoverinfo', [['skip', 'name', 'x']]); + }) + .then(function () { + _hover(gd, { xval: gd._fullData[0].x[0] }); - assertPtLabel(null); - assertCommonLabel(null); - }) - .then(function() { - _hover(gd, { xval: gd._fullData[0].x[1] }); + assertPtLabel(null); + assertCommonLabel(null); + }) + .then(function () { + _hover(gd, { xval: gd._fullData[0].x[1] }); - assertPtLabel({ - path: ['rgb(255, 255, 255)', 'rgb(68, 68, 68)'], - text: [20, 'Arial', 'rgb(0, 128, 0)'] - }); - assertCommonLabel(null); - }) - .then(function() { - _hover(gd, { xval: gd._fullData[0].x[2] }); + assertPtLabel({ + path: ['rgb(255, 255, 255)', 'rgb(68, 68, 68)'], + text: [20, 'Arial', 'rgb(0, 128, 0)'] + }); + assertCommonLabel(null); + }) + .then(function () { + _hover(gd, { xval: gd._fullData[0].x[2] }); - assertPtLabel(null); - assertCommonLabel({ - path: ['rgb(255, 255, 255)', 'rgb(68, 68, 68)'], - text: [13, 'Arial', 'rgb(68, 68, 68)'] - }); + assertPtLabel(null); + assertCommonLabel({ + path: ['rgb(255, 255, 255)', 'rgb(68, 68, 68)'], + text: [13, 'Arial', 'rgb(68, 68, 68)'] + }); - // test base case - return Plotly.update(gd, { - hoverlabel: null, - // all these items should be display as 'all' - hoverinfo: [['i+dont+what+im+doing', null, undefined]] - }, { - hoverlabel: null - }); - }) - .then(function() { - _hover(gd, { xval: gd._fullData[0].x[0] }); + // test base case + return Plotly.update( + gd, + { + hoverlabel: null, + // all these items should be display as 'all' + hoverinfo: [['i+dont+what+im+doing', null, undefined]] + }, + { + hoverlabel: null + } + ); + }) + .then(function () { + _hover(gd, { xval: gd._fullData[0].x[0] }); - assertPtLabel({ - path: ['rgb(255, 255, 0)', 'rgb(68, 68, 68)'], - text: [13, 'Arial', 'rgb(68, 68, 68)'] - }); - assertCommonLabel({ - path: ['rgb(68, 68, 68)', 'rgb(255, 255, 255)'], - text: [13, 'Arial', 'rgb(255, 255, 255)'] - }); - }) - .then(function() { - _hover(gd, { xval: gd._fullData[0].x[1] }); + assertPtLabel({ + path: ['rgb(255, 255, 0)', 'rgb(68, 68, 68)'], + text: [13, 'Arial', 'rgb(68, 68, 68)'] + }); + assertCommonLabel({ + path: ['rgb(68, 68, 68)', 'rgb(255, 255, 255)'], + text: [13, 'Arial', 'rgb(255, 255, 255)'] + }); + }) + .then(function () { + _hover(gd, { xval: gd._fullData[0].x[1] }); - assertPtLabel({ - path: ['rgb(0, 0, 0)', 'rgb(255, 255, 255)'], - text: [13, 'Arial', 'rgb(255, 255, 255)'] - }); - assertCommonLabel({ - path: ['rgb(68, 68, 68)', 'rgb(255, 255, 255)'], - text: [13, 'Arial', 'rgb(255, 255, 255)'] - }); - }) - .then(function() { - _hover(gd, { xval: gd._fullData[0].x[2] }); + assertPtLabel({ + path: ['rgb(0, 0, 0)', 'rgb(255, 255, 255)'], + text: [13, 'Arial', 'rgb(255, 255, 255)'] + }); + assertCommonLabel({ + path: ['rgb(68, 68, 68)', 'rgb(255, 255, 255)'], + text: [13, 'Arial', 'rgb(255, 255, 255)'] + }); + }) + .then(function () { + _hover(gd, { xval: gd._fullData[0].x[2] }); - assertPtLabel({ - path: ['rgb(0, 255, 255)', 'rgb(68, 68, 68)'], - text: [13, 'Arial', 'rgb(68, 68, 68)'] - }); - assertCommonLabel({ - path: ['rgb(68, 68, 68)', 'rgb(255, 255, 255)'], - text: [13, 'Arial', 'rgb(255, 255, 255)'] - }); + assertPtLabel({ + path: ['rgb(0, 255, 255)', 'rgb(68, 68, 68)'], + text: [13, 'Arial', 'rgb(68, 68, 68)'] + }); + assertCommonLabel({ + path: ['rgb(68, 68, 68)', 'rgb(255, 255, 255)'], + text: [13, 'Arial', 'rgb(255, 255, 255)'] + }); - // test insufficient arrayOk case - return Plotly.restyle(gd, 'hoverinfo', [['none']]); - }) - .then(function() { - expect(gd.calcdata[0].map(function(o) { return o.hi; })).toEqual( - ['none', 'x+y+z+text', 'x+y+z+text'], - 'should fill calcdata item with correct default' - ); + // test insufficient arrayOk case + return Plotly.restyle(gd, 'hoverinfo', [['none']]); + }) + .then(function () { + expect( + gd.calcdata[0].map(function (o) { + return o.hi; + }) + ).toEqual(['none', 'x+y+z+text', 'x+y+z+text'], 'should fill calcdata item with correct default'); - _hover(gd, { xval: gd._fullData[0].x[0] }); + _hover(gd, { xval: gd._fullData[0].x[0] }); - assertPtLabel(null); - assertCommonLabel(null); - }) - .then(function() { - _hover(gd, { xval: gd._fullData[0].x[1] }); + assertPtLabel(null); + assertCommonLabel(null); + }) + .then(function () { + _hover(gd, { xval: gd._fullData[0].x[1] }); - assertPtLabel({ - path: ['rgb(0, 0, 0)', 'rgb(255, 255, 255)'], - text: [13, 'Arial', 'rgb(255, 255, 255)'] - }); - assertCommonLabel({ - path: ['rgb(68, 68, 68)', 'rgb(255, 255, 255)'], - text: [13, 'Arial', 'rgb(255, 255, 255)'] - }); - }) - .then(function() { - _hover(gd, { xval: gd._fullData[0].x[2] }); + assertPtLabel({ + path: ['rgb(0, 0, 0)', 'rgb(255, 255, 255)'], + text: [13, 'Arial', 'rgb(255, 255, 255)'] + }); + assertCommonLabel({ + path: ['rgb(68, 68, 68)', 'rgb(255, 255, 255)'], + text: [13, 'Arial', 'rgb(255, 255, 255)'] + }); + }) + .then(function () { + _hover(gd, { xval: gd._fullData[0].x[2] }); - assertPtLabel({ - path: ['rgb(0, 255, 255)', 'rgb(68, 68, 68)'], - text: [13, 'Arial', 'rgb(68, 68, 68)'] - }); - assertCommonLabel({ - path: ['rgb(68, 68, 68)', 'rgb(255, 255, 255)'], - text: [13, 'Arial', 'rgb(255, 255, 255)'] - }); - }) - .then(done, done.fail); + assertPtLabel({ + path: ['rgb(0, 255, 255)', 'rgb(68, 68, 68)'], + text: [13, 'Arial', 'rgb(68, 68, 68)'] + }); + assertCommonLabel({ + path: ['rgb(68, 68, 68)', 'rgb(255, 255, 255)'], + text: [13, 'Arial', 'rgb(255, 255, 255)'] + }); + }) + .then(done, done.fail); }); - it('should work for x/y cartesian traces (multi-trace case)', function(done) { + it('should work for x/y cartesian traces (multi-trace case)', function (done) { var gd = createGraphDiv(); function assertNameLabel(expectation) { var g = d3SelectAll('g.hovertext > text.name'); - if(expectation === null) { + if (expectation === null) { expect(g.size()).toBe(0); } else { - g.each(function(_, i) { + g.each(function (_, i) { var textStyle = window.getComputedStyle(this); expect(textStyle.fill).toBe(expectation.color[i]); }); } } - Plotly.newPlot(gd, [{ - x: [1, 2, 3], - y: [1, 2, 1], - }, { - x: [1, 2, 3], - y: [4, 5, 4], - }], { - hovermode: 'x', - }) - .then(function() { - _hover(gd, { xval: gd._fullData[0].x[0] }); - assertNameLabel({ - color: ['rgb(31, 119, 180)', 'rgb(255, 127, 14)'] - }); - return Plotly.restyle(gd, 'marker.color', ['red', 'blue']); - }) - .then(function() { - _hover(gd, { xval: gd._fullData[0].x[0] }); - assertNameLabel({ - color: ['rgb(255, 0, 0)', 'rgb(0, 0, 255)'] - }); - return Plotly.relayout(gd, 'hoverlabel.bgcolor', 'white'); - }) - .then(function() { - _hover(gd, { xval: gd._fullData[0].x[0] }); - // should not affect the name font color - assertNameLabel({ - color: ['rgb(255, 0, 0)', 'rgb(0, 0, 255)'] - }); - return Plotly.restyle(gd, 'marker.color', ['rgba(255,0,0,0.1)', 'rgba(0,0,255,0.1)']); - }) - .then(function() { - _hover(gd, { xval: gd._fullData[0].x[0] }); - // should blend with plot_bgcolor - assertNameLabel({ - color: ['rgb(255, 179, 179)', 'rgb(179, 179, 255)'] - }); - return Plotly.restyle(gd, 'marker.color', ['rgba(255,0,0,0)', 'rgba(0,0,255,0)']); - }) - .then(function() { - _hover(gd, { xval: gd._fullData[0].x[0] }); - // uses default line color when opacity=0 - assertNameLabel({ - color: ['rgb(68, 68, 68)', 'rgb(68, 68, 68)'] - }); - }) - .then(done, done.fail); + Plotly.newPlot( + gd, + [ + { + x: [1, 2, 3], + y: [1, 2, 1] + }, + { + x: [1, 2, 3], + y: [4, 5, 4] + } + ], + { + hovermode: 'x' + } + ) + .then(function () { + _hover(gd, { xval: gd._fullData[0].x[0] }); + assertNameLabel({ + color: ['rgb(31, 119, 180)', 'rgb(255, 127, 14)'] + }); + return Plotly.restyle(gd, 'marker.color', ['red', 'blue']); + }) + .then(function () { + _hover(gd, { xval: gd._fullData[0].x[0] }); + assertNameLabel({ + color: ['rgb(255, 0, 0)', 'rgb(0, 0, 255)'] + }); + return Plotly.relayout(gd, 'hoverlabel.bgcolor', 'white'); + }) + .then(function () { + _hover(gd, { xval: gd._fullData[0].x[0] }); + // should not affect the name font color + assertNameLabel({ + color: ['rgb(255, 0, 0)', 'rgb(0, 0, 255)'] + }); + return Plotly.restyle(gd, 'marker.color', ['rgba(255,0,0,0.1)', 'rgba(0,0,255,0.1)']); + }) + .then(function () { + _hover(gd, { xval: gd._fullData[0].x[0] }); + // should blend with plot_bgcolor + assertNameLabel({ + color: ['rgb(255, 179, 179)', 'rgb(179, 179, 255)'] + }); + return Plotly.restyle(gd, 'marker.color', ['rgba(255,0,0,0)', 'rgba(0,0,255,0)']); + }) + .then(function () { + _hover(gd, { xval: gd._fullData[0].x[0] }); + // uses default line color when opacity=0 + assertNameLabel({ + color: ['rgb(68, 68, 68)', 'rgb(68, 68, 68)'] + }); + }) + .then(done, done.fail); }); - it('should work for 2d z cartesian traces', function(done) { + it('should work for 2d z cartesian traces', function (done) { var gd = createGraphDiv(); - Plotly.newPlot(gd, [{ - type: 'heatmap', - x: [1, 2], - y: [1, 2], - z: [[1, 2], [2, 3]], - hoverlabel: { - font: { - color: 'red', - size: [[10, 20], [21, 11]] + Plotly.newPlot( + gd, + [ + { + type: 'heatmap', + x: [1, 2], + y: [1, 2], + z: [ + [1, 2], + [2, 3] + ], + hoverlabel: { + font: { + color: 'red', + size: [ + [10, 20], + [21, 11] + ] + } + } + } + ], + { + hoverlabel: { + bordercolor: 'blue', + font: { family: 'Gravitas' } } } - }], { - hoverlabel: { - bordercolor: 'blue', - font: { family: 'Gravitas'} - } - }) - .then(function() { - _hover(gd, { xval: 1, yval: 1 }); + ) + .then(function () { + _hover(gd, { xval: 1, yval: 1 }); - assertPtLabel({ - path: ['rgb(68, 68, 68)', 'rgb(0, 0, 255)'], - text: [10, 'Gravitas', 'rgb(255, 0, 0)'] - }); - }) - .then(function() { - _hover(gd, { xval: 2, yval: 1 }); + assertPtLabel({ + path: ['rgb(68, 68, 68)', 'rgb(0, 0, 255)'], + text: [10, 'Gravitas', 'rgb(255, 0, 0)'] + }); + }) + .then(function () { + _hover(gd, { xval: 2, yval: 1 }); - assertPtLabel({ - path: ['rgb(68, 68, 68)', 'rgb(0, 0, 255)'], - text: [20, 'Gravitas', 'rgb(255, 0, 0)'] - }); - }) - .then(function() { - _hover(gd, { xval: 1, yval: 2 }); + assertPtLabel({ + path: ['rgb(68, 68, 68)', 'rgb(0, 0, 255)'], + text: [20, 'Gravitas', 'rgb(255, 0, 0)'] + }); + }) + .then(function () { + _hover(gd, { xval: 1, yval: 2 }); - assertPtLabel({ - path: ['rgb(68, 68, 68)', 'rgb(0, 0, 255)'], - text: [21, 'Gravitas', 'rgb(255, 0, 0)'] - }); - }) - .then(function() { - _hover(gd, { xval: 2, yval: 2 }); + assertPtLabel({ + path: ['rgb(68, 68, 68)', 'rgb(0, 0, 255)'], + text: [21, 'Gravitas', 'rgb(255, 0, 0)'] + }); + }) + .then(function () { + _hover(gd, { xval: 2, yval: 2 }); - assertPtLabel({ - path: ['rgb(68, 68, 68)', 'rgb(0, 0, 255)'], - text: [11, 'Gravitas', 'rgb(255, 0, 0)'] - }); - }) - .then(done, done.fail); + assertPtLabel({ + path: ['rgb(68, 68, 68)', 'rgb(0, 0, 255)'], + text: [11, 'Gravitas', 'rgb(255, 0, 0)'] + }); + }) + .then(done, done.fail); }); }); -describe('hover distance', function() { +describe('hover distance', function () { 'use strict'; var mock = require('../../image/mocks/19.json'); afterEach(destroyGraphDiv); - describe('closest hovermode', function() { + describe('closest hovermode', function () { var mockCopy = Lib.extendDeep({}, mock); mockCopy.layout.hovermode = 'closest'; // use simple markers here delete mockCopy.data[0].marker; delete mockCopy.data[1].marker; - beforeEach(function(done) { + beforeEach(function (done) { Plotly.newPlot(createGraphDiv(), mockCopy.data, mockCopy.layout).then(done); }); - it('does not render if distance to the point is larger than default (>20)', function() { + it('does not render if distance to the point is larger than default (>20)', function () { var gd = document.getElementById('graph'); var evt = { xpx: 475, ypx: 175 }; Fx.hover('graph', evt, 'xy'); @@ -4697,7 +5198,7 @@ describe('hover distance', function() { expect(gd._hoverdata).toEqual(undefined); }); - it('render if distance to the point is less than default (<20)', function() { + it('render if distance to the point is less than default (<20)', function () { var gd = document.getElementById('graph'); var evt = { xpx: 475, ypx: 155 }; Fx.hover('graph', evt, 'xy'); @@ -4715,61 +5216,64 @@ describe('hover distance', function() { }); }); - it('responds to hoverdistance change', function(done) { + it('responds to hoverdistance change', function (done) { var gd = document.getElementById('graph'); var evt = { xpx: 475, ypx: 180 }; Plotly.relayout(gd, 'hoverdistance', 30) - .then(function() { - Fx.hover('graph', evt, 'xy'); + .then(function () { + Fx.hover('graph', evt, 'xy'); - var hoverTrace = gd._hoverdata[0]; + var hoverTrace = gd._hoverdata[0]; - expect(hoverTrace.curveNumber).toEqual(0); - expect(hoverTrace.pointNumber).toEqual(1); - expect(hoverTrace.x).toEqual(2); - expect(hoverTrace.y).toEqual(3); + expect(hoverTrace.curveNumber).toEqual(0); + expect(hoverTrace.pointNumber).toEqual(1); + expect(hoverTrace.x).toEqual(2); + expect(hoverTrace.y).toEqual(3); - assertHoverLabelContent({ - nums: '(2, 3)', - name: 'trace 0' - }); - }) - .then(done, done.fail); + assertHoverLabelContent({ + nums: '(2, 3)', + name: 'trace 0' + }); + }) + .then(done, done.fail); }); - it('correctly responds to setting the hoverdistance to -1 by increasing ' + - 'the range of search for points to hover to Infinity', function(done) { - var gd = document.getElementById('graph'); - var evt = { xpx: 475, ypx: 180 }; - Plotly.relayout(gd, 'hoverdistance', -1) - .then(function() { - Fx.hover('graph', evt, 'xy'); + it( + 'correctly responds to setting the hoverdistance to -1 by increasing ' + + 'the range of search for points to hover to Infinity', + function (done) { + var gd = document.getElementById('graph'); + var evt = { xpx: 475, ypx: 180 }; + Plotly.relayout(gd, 'hoverdistance', -1) + .then(function () { + Fx.hover('graph', evt, 'xy'); - var hoverTrace = gd._hoverdata[0]; + var hoverTrace = gd._hoverdata[0]; - expect(hoverTrace.curveNumber).toEqual(0); - expect(hoverTrace.pointNumber).toEqual(1); - expect(hoverTrace.x).toEqual(2); - expect(hoverTrace.y).toEqual(3); + expect(hoverTrace.curveNumber).toEqual(0); + expect(hoverTrace.pointNumber).toEqual(1); + expect(hoverTrace.x).toEqual(2); + expect(hoverTrace.y).toEqual(3); - assertHoverLabelContent({ - nums: '(2, 3)', - name: 'trace 0' - }); - }) - .then(done, done.fail); - }); + assertHoverLabelContent({ + nums: '(2, 3)', + name: 'trace 0' + }); + }) + .then(done, done.fail); + } + ); }); - describe('x hovermode', function() { + describe('x hovermode', function () { var mockCopy = Lib.extendDeep({}, mock); mockCopy.layout.hovermode = 'x'; - beforeEach(function(done) { + beforeEach(function (done) { Plotly.newPlot(createGraphDiv(), mockCopy.data, mockCopy.layout).then(done); }); - it('does not render if distance to the point is larger than default (>20)', function() { + it('does not render if distance to the point is larger than default (>20)', function () { var gd = document.getElementById('graph'); var evt = { xpx: 450, ypx: 155 }; Fx.hover('graph', evt, 'xy'); @@ -4777,7 +5281,7 @@ describe('hover distance', function() { expect(gd._hoverdata).toEqual(undefined); }); - it('render if distance to the point is less than default (<20)', function() { + it('render if distance to the point is less than default (<20)', function () { var gd = document.getElementById('graph'); var evt = { xpx: 475, ypx: 155 }; Fx.hover('graph', evt, 'xy'); @@ -4796,42 +5300,42 @@ describe('hover distance', function() { }); }); - it('responds to hoverdistance change from 10 to 30 (part 1)', function(done) { + it('responds to hoverdistance change from 10 to 30 (part 1)', function (done) { var gd = document.getElementById('graph'); var evt = { xpx: 450, ypx: 155 }; Plotly.relayout(gd, 'hoverdistance', 10) - .then(function() { - Fx.hover('graph', evt, 'xy'); + .then(function () { + Fx.hover('graph', evt, 'xy'); - expect(gd._hoverdata).toEqual(undefined); - }) - .then(done, done.fail); + expect(gd._hoverdata).toEqual(undefined); + }) + .then(done, done.fail); }); - it('responds to hoverdistance change from 10 to 30 (part 2)', function(done) { + it('responds to hoverdistance change from 10 to 30 (part 2)', function (done) { var gd = document.getElementById('graph'); var evt = { xpx: 450, ypx: 155 }; Plotly.relayout(gd, 'hoverdistance', 30) - .then(function() { - Fx.hover('graph', evt, 'xy'); + .then(function () { + Fx.hover('graph', evt, 'xy'); - var hoverTrace = gd._hoverdata[0]; + var hoverTrace = gd._hoverdata[0]; - expect(hoverTrace.curveNumber).toEqual(0); - expect(hoverTrace.pointNumber).toEqual(1); - expect(hoverTrace.x).toEqual(2); - expect(hoverTrace.y).toEqual(3); + expect(hoverTrace.curveNumber).toEqual(0); + expect(hoverTrace.pointNumber).toEqual(1); + expect(hoverTrace.x).toEqual(2); + expect(hoverTrace.y).toEqual(3); - assertHoverLabelContent({ - nums: '3', - axis: '2', - name: 'trace 0' - }); - }) - .then(done, done.fail); + assertHoverLabelContent({ + nums: '3', + axis: '2', + name: 'trace 0' + }); + }) + .then(done, done.fail); }); - it('responds to hoverdistance change from default to 0 (part 1)', function() { + it('responds to hoverdistance change from default to 0 (part 1)', function () { var gd = document.getElementById('graph'); var evt = { xpx: 475, ypx: 155 }; Fx.hover('graph', evt, 'xy'); @@ -4850,155 +5354,176 @@ describe('hover distance', function() { }); }); - it('responds to hoverdistance change from default to 0 (part 2)', function(done) { + it('responds to hoverdistance change from default to 0 (part 2)', function (done) { var gd = document.getElementById('graph'); var evt = { xpx: 475, ypx: 155 }; Plotly.relayout(gd, 'hoverdistance', 0) - .then(function() { - Fx.hover('graph', evt, 'xy'); - - expect(gd._hoverdata).toEqual(undefined); - }) - .then(done, done.fail); - }); - - it('responds to setting the hoverdistance to -1 by increasing ' + - 'the range of search for points to hover to Infinity (part 1)', function() { - var gd = document.getElementById('graph'); - var evt = { xpx: 450, ypx: 155 }; - Fx.hover('graph', evt, 'xy'); + .then(function () { + Fx.hover('graph', evt, 'xy'); - expect(gd._hoverdata).toEqual(undefined); + expect(gd._hoverdata).toEqual(undefined); + }) + .then(done, done.fail); }); - it('responds to setting the hoverdistance to -1 by increasing ' + - 'the range of search for points to hover to Infinity (part 2)', function(done) { - var gd = document.getElementById('graph'); - var evt = { xpx: 450, ypx: 155 }; - Plotly.relayout(gd, 'hoverdistance', -1) - .then(function() { + it( + 'responds to setting the hoverdistance to -1 by increasing ' + + 'the range of search for points to hover to Infinity (part 1)', + function () { + var gd = document.getElementById('graph'); + var evt = { xpx: 450, ypx: 155 }; Fx.hover('graph', evt, 'xy'); - var hoverTrace = gd._hoverdata[0]; - - expect(hoverTrace.curveNumber).toEqual(0); - expect(hoverTrace.pointNumber).toEqual(1); - expect(hoverTrace.x).toEqual(2); - expect(hoverTrace.y).toEqual(3); - - assertHoverLabelContent({ - nums: '(2, 3)', - name: 'trace 0' - }); - }) - .then(done, done.fail); - }); + expect(gd._hoverdata).toEqual(undefined); + } + ); + + it( + 'responds to setting the hoverdistance to -1 by increasing ' + + 'the range of search for points to hover to Infinity (part 2)', + function (done) { + var gd = document.getElementById('graph'); + var evt = { xpx: 450, ypx: 155 }; + Plotly.relayout(gd, 'hoverdistance', -1) + .then(function () { + Fx.hover('graph', evt, 'xy'); + + var hoverTrace = gd._hoverdata[0]; + + expect(hoverTrace.curveNumber).toEqual(0); + expect(hoverTrace.pointNumber).toEqual(1); + expect(hoverTrace.x).toEqual(2); + expect(hoverTrace.y).toEqual(3); + + assertHoverLabelContent({ + nums: '(2, 3)', + name: 'trace 0' + }); + }) + .then(done, done.fail); + } + ); }); - describe('compare', function() { + describe('compare', function () { var gd; - beforeEach(function() { + beforeEach(function () { gd = createGraphDiv(); }); afterEach(destroyGraphDiv); - it('selects all points at the same position on a linear axis', function(done) { + it('selects all points at the same position on a linear axis', function (done) { var x = [0, 1, 2]; var mock = { - data: [{type: 'bar', x: x, y: [4, 5, 6]}, {x: x, y: [5, 6, 7]}], - layout: {width: 400, height: 400, xaxis: {type: 'linear'}, hovermode: 'x'} + data: [ + { type: 'bar', x: x, y: [4, 5, 6] }, + { x: x, y: [5, 6, 7] } + ], + layout: { width: 400, height: 400, xaxis: { type: 'linear' }, hovermode: 'x' } }; Plotly.newPlot(gd, mock) - .then(function(gd) { - Fx.hover(gd, {xpx: 65}); + .then(function (gd) { + Fx.hover(gd, { xpx: 65 }); expect(gd._hoverdata.length).toEqual(2); }) .then(done, done.fail); }); - it('selects all points at the same position on a log axis', function(done) { + it('selects all points at the same position on a log axis', function (done) { var x = [0, 1, 2]; var mock = { - data: [{type: 'bar', x: x, y: [4, 5, 6]}, {x: x, y: [5, 6, 7]}], - layout: {width: 400, height: 400, xaxis: {type: 'log'}, hovermode: 'x'} + data: [ + { type: 'bar', x: x, y: [4, 5, 6] }, + { x: x, y: [5, 6, 7] } + ], + layout: { width: 400, height: 400, xaxis: { type: 'log' }, hovermode: 'x' } }; Plotly.newPlot(gd, mock) - .then(function(gd) { - Fx.hover(gd, {xpx: 65}); + .then(function (gd) { + Fx.hover(gd, { xpx: 65 }); expect(gd._hoverdata.length).toEqual(2); }) .then(done, done.fail); }); - it('selects all points at the same position on a category axis', function(done) { + it('selects all points at the same position on a category axis', function (done) { var x = ['a', 'b', 'c']; var mock = { - data: [{type: 'bar', x: x, y: [4, 5, 6]}, {x: x, y: [5, 6, 7]}], - layout: {width: 400, height: 400, xaxis: {type: 'category'}, hovermode: 'x'} + data: [ + { type: 'bar', x: x, y: [4, 5, 6] }, + { x: x, y: [5, 6, 7] } + ], + layout: { width: 400, height: 400, xaxis: { type: 'category' }, hovermode: 'x' } }; Plotly.newPlot(gd, mock) - .then(function(gd) { - Fx.hover(gd, {xpx: 65}); + .then(function (gd) { + Fx.hover(gd, { xpx: 65 }); expect(gd._hoverdata.length).toEqual(2); }) .then(done, done.fail); }); - it('selects all points at the same position on a date axis', function(done) { + it('selects all points at the same position on a date axis', function (done) { var x = ['2018', '2019', '2020']; var mock = { - data: [{type: 'bar', x: x, y: [4, 5, 6]}, {x: x, y: [5, 6, 7]}], - layout: {width: 400, height: 400, xaxis: {type: 'date'}, hovermode: 'x'} + data: [ + { type: 'bar', x: x, y: [4, 5, 6] }, + { x: x, y: [5, 6, 7] } + ], + layout: { width: 400, height: 400, xaxis: { type: 'date' }, hovermode: 'x' } }; Plotly.newPlot(gd, mock) - .then(function(gd) { - Fx.hover(gd, {xpx: 65}); + .then(function (gd) { + Fx.hover(gd, { xpx: 65 }); expect(gd._hoverdata.length).toEqual(2); }) .then(done, done.fail); }); - it('correctly format the epoch timestamp in a given hover format', function(done) { + it('correctly format the epoch timestamp in a given hover format', function (done) { var x = ['1970-01-01 00:00:00']; var mock = { - data: [{type: 'scatter', x: x, y: [1]}], - layout: {width: 400, height: 400, xaxis: {hoverformat: '%H:%M:%S'}} + data: [{ type: 'scatter', x: x, y: [1] }], + layout: { width: 400, height: 400, xaxis: { hoverformat: '%H:%M:%S' } } }; Plotly.newPlot(gd, mock) - .then(function(gd) { - Fx.hover(gd, {xpx: 120, ypx: 110}); - assertHoverLabelContent({nums: '(00:00:00, 1)'}); + .then(function (gd) { + Fx.hover(gd, { xpx: 120, ypx: 110 }); + assertHoverLabelContent({ nums: '(00:00:00, 1)' }); }) .then(done, done.fail); }); }); }); -describe('hover working with zorder', function() { +describe('hover working with zorder', function () { 'use strict'; var mock = { - data: [{ - zorder: 100, - marker: {size: 50}, - text: ['A', 'B'], - y: [0, 1] - }, { - marker: {size: 50}, - text: ['C', 'D'], - y: [2, 1] - }], + data: [ + { + zorder: 100, + marker: { size: 50 }, + text: ['A', 'B'], + y: [0, 1] + }, + { + marker: { size: 50 }, + text: ['C', 'D'], + y: [2, 1] + } + ], layout: { width: 400, height: 400, @@ -5009,13 +5534,13 @@ describe('hover working with zorder', function() { afterEach(destroyGraphDiv); - beforeEach(function(done) { + beforeEach(function (done) { Plotly.newPlot(createGraphDiv(), mock).then(done); }); - it('pick the trace on top', function() { + it('pick the trace on top', function () { var gd = document.getElementById('graph'); - Fx.hover('graph', {xval: 1}, 'xy'); + Fx.hover('graph', { xval: 1 }, 'xy'); expect(gd._hoverdata.length).toEqual(1); @@ -5031,7 +5556,7 @@ describe('hover working with zorder', function() { }); }); -describe('hover label rotation:', function() { +describe('hover label rotation:', function () { var gd; function _hover(gd, opts) { @@ -5039,29 +5564,35 @@ describe('hover label rotation:', function() { Lib.clearThrottle(); } - describe('when a single pt is picked', function() { + describe('when a single pt is picked', function () { afterAll(destroyGraphDiv); - beforeAll(function(done) { + beforeAll(function (done) { gd = createGraphDiv(); - Plotly.newPlot(gd, [{ - type: 'bar', - orientation: 'h', - y: [0, 1, 2], - x: [1, 2, 1] - }, { - type: 'bar', - orientation: 'h', - y: [3, 4, 5], - x: [1, 2, 1] - }], { - hovermode: 'y' - }) - .then(done, done.fail); + Plotly.newPlot( + gd, + [ + { + type: 'bar', + orientation: 'h', + y: [0, 1, 2], + x: [1, 2, 1] + }, + { + type: 'bar', + orientation: 'h', + y: [3, 4, 5], + x: [1, 2, 1] + } + ], + { + hovermode: 'y' + } + ).then(done, done.fail); }); - it('should rotate labels under *hovermode:y*', function() { + it('should rotate labels under *hovermode:y*', function () { _hover(gd, { xval: 2, yval: 1 }); assertHoverLabelContent({ nums: '2', @@ -5072,44 +5603,50 @@ describe('hover label rotation:', function() { }); }); - it('should not rotate labels under *hovermode:closest*', function(done) { + it('should not rotate labels under *hovermode:closest*', function (done) { Plotly.relayout(gd, 'hovermode', 'closest') - .then(function() { - _hover(gd, { xval: 1.9, yval: 1 }); - assertHoverLabelContent({ - nums: '(2, 1)', - name: 'trace 0', - axis: '', - isRotated: false - }); - }) - .then(done, done.fail); + .then(function () { + _hover(gd, { xval: 1.9, yval: 1 }); + assertHoverLabelContent({ + nums: '(2, 1)', + name: 'trace 0', + axis: '', + isRotated: false + }); + }) + .then(done, done.fail); }); }); - describe('when multiple pts are picked', function() { + describe('when multiple pts are picked', function () { afterAll(destroyGraphDiv); - beforeAll(function(done) { + beforeAll(function (done) { gd = createGraphDiv(); - Plotly.newPlot(gd, [{ - type: 'bar', - orientation: 'h', - y: [0, 1, 2], - x: [1, 2, 1] - }, { - type: 'bar', - orientation: 'h', - y: [0, 1, 2], - x: [1, 2, 1] - }], { - hovermode: 'y' - }) - .then(done, done.fail); + Plotly.newPlot( + gd, + [ + { + type: 'bar', + orientation: 'h', + y: [0, 1, 2], + x: [1, 2, 1] + }, + { + type: 'bar', + orientation: 'h', + y: [0, 1, 2], + x: [1, 2, 1] + } + ], + { + hovermode: 'y' + } + ).then(done, done.fail); }); - it('should rotate labels under *hovermode:y*', function() { + it('should rotate labels under *hovermode:y*', function () { _hover(gd, { xval: 2, yval: 1 }); assertHoverLabelContent({ nums: ['2', '2'], @@ -5119,68 +5656,72 @@ describe('hover label rotation:', function() { }); }); - it('should not rotate labels under *hovermode:closest*', function(done) { + it('should not rotate labels under *hovermode:closest*', function (done) { Plotly.relayout(gd, 'hovermode', 'closest') - .then(function() { - _hover(gd, { xval: 1.9, yval: 1 }); - assertHoverLabelContent({ - nums: '(2, 1)', - // N.B. only showing the 'top' trace - name: 'trace 1', - axis: '', - isRotated: false - }); - }) - .then(done, done.fail); + .then(function () { + _hover(gd, { xval: 1.9, yval: 1 }); + assertHoverLabelContent({ + nums: '(2, 1)', + // N.B. only showing the 'top' trace + name: 'trace 1', + axis: '', + isRotated: false + }); + }) + .then(done, done.fail); }); }); }); -describe('hover labels z-position', function() { +describe('hover labels z-position', function () { var gd; - beforeEach(function() { + beforeEach(function () { gd = createGraphDiv(); }); afterEach(destroyGraphDiv); var mock = require('../../image/mocks/14.json'); - it('is above the modebar', function(done) { - Plotly.newPlot(gd, mock).then(function() { - var infolayer = document.getElementsByClassName('infolayer'); - var modebar = document.getElementsByClassName('modebar-container'); - var hoverlayer = document.getElementsByClassName('hoverlayer'); + it('is above the modebar', function (done) { + Plotly.newPlot(gd, mock) + .then(function () { + var infolayer = document.getElementsByClassName('infolayer'); + var modebar = document.getElementsByClassName('modebar-container'); + var hoverlayer = document.getElementsByClassName('hoverlayer'); - expect(infolayer.length).toBe(1); - expect(modebar.length).toBe(1); - expect(hoverlayer.length).toBe(1); + expect(infolayer.length).toBe(1); + expect(modebar.length).toBe(1); + expect(hoverlayer.length).toBe(1); - var compareMask = infolayer[0].compareDocumentPosition(modebar[0]); - expect(compareMask).toBe(Node.DOCUMENT_POSITION_FOLLOWING, '.modebar-container appears after the .infolayer'); + var compareMask = infolayer[0].compareDocumentPosition(modebar[0]); + expect(compareMask).toBe( + Node.DOCUMENT_POSITION_FOLLOWING, + '.modebar-container appears after the .infolayer' + ); - compareMask = modebar[0].compareDocumentPosition(hoverlayer[0]); - expect(compareMask).toBe(Node.DOCUMENT_POSITION_FOLLOWING, '.hoverlayer appears after the .modebar'); - }) - .then(done, done.fail); + compareMask = modebar[0].compareDocumentPosition(hoverlayer[0]); + expect(compareMask).toBe(Node.DOCUMENT_POSITION_FOLLOWING, '.hoverlayer appears after the .modebar'); + }) + .then(done, done.fail); }); }); -describe('touch devices', function() { +describe('touch devices', function () { afterEach(destroyGraphDiv); - ['pan', 'zoom'].forEach(function(type) { - describe('dragmode:' + type, function() { - var data = [{x: [1, 2, 3], y: [1, 3, 2], type: 'bar'}]; - var layout = {width: 600, height: 400, dragmode: type}; + ['pan', 'zoom'].forEach(function (type) { + describe('dragmode:' + type, function () { + var data = [{ x: [1, 2, 3], y: [1, 3, 2], type: 'bar' }]; + var layout = { width: 600, height: 400, dragmode: type }; var gd; - beforeEach(function(done) { + beforeEach(function (done) { gd = createGraphDiv(); Plotly.newPlot(gd, data, layout).then(done); }); - it('emits click events', function(done) { + it('emits click events', function (done) { var hoverHandler = jasmine.createSpy('hover'); var clickHandler = jasmine.createSpy('click'); gd.on('plotly_hover', hoverHandler); @@ -5190,11 +5731,11 @@ describe('touch devices', function() { var touchPoint = [[gdBB.left + 300, gdBB.top + 200]]; Promise.resolve() - .then(function() { + .then(function () { touch(touchPoint); }) .then(delay(HOVERMINTIME * 1.1)) - .then(function() { + .then(function () { expect(clickHandler).toHaveBeenCalled(); expect(hoverHandler).not.toHaveBeenCalled(); }) @@ -5204,42 +5745,41 @@ describe('touch devices', function() { }); }); -describe('dragmode: false', function() { - var data = [{x: [1, 2, 3], y: [1, 3, 2], type: 'bar'}]; - var layout = {width: 600, height: 400, dragmode: false}; +describe('dragmode: false', function () { + var data = [{ x: [1, 2, 3], y: [1, 3, 2], type: 'bar' }]; + var layout = { width: 600, height: 400, dragmode: false }; var gd; - beforeEach(function(done) { + beforeEach(function (done) { gd = createGraphDiv(); Plotly.newPlot(gd, data, layout).then(done); }); afterEach(destroyGraphDiv); - it('should emit hover events on mousemove', function(done) { + it('should emit hover events on mousemove', function (done) { var hoverHandler = jasmine.createSpy('hover'); gd.on('plotly_hover', hoverHandler); Promise.resolve() - .then(function() { + .then(function () { mouseEvent('mousemove', 105, 300); mouseEvent('mousemove', 108, 303); }) .then(delay(HOVERMINTIME * 1.1)) - .then(function() { + .then(function () { expect(hoverHandler).toHaveBeenCalled(); }) .then(done, done.fail); }); }); -describe('hovermode: (x|y)unified', function() { +describe('hovermode: (x|y)unified', function () { var gd; var mock = { - data: [ - {y: [0, 3, 6, 4, 10, 2, 3, 5, 4, 0, 5]}, - {y: [0, 4, 7, 8, 10, 6, 3, 3, 4, 0, 5], } - ], layout: {showlegend: false, hovermode: 'x unified'}}; + data: [{ y: [0, 3, 6, 4, 10, 2, 3, 5, 4, 0, 5] }, { y: [0, 4, 7, 8, 10, 6, 3, 3, 4, 0, 5] }], + layout: { showlegend: false, hovermode: 'x unified' } + }; - beforeEach(function() { + beforeEach(function () { gd = createGraphDiv(); }); @@ -5265,12 +5805,12 @@ describe('hovermode: (x|y)unified', function() { var title = hover.select('text.legendtitletext'); var traces = hover.selectAll('g.traces'); - if(expectation.title) { + if (expectation.title) { expect(title.text()).toBe(expectation.title); } expect(traces.size()).toBe(expectation.items.length, 'has the incorrect number of items'); - traces.each(function(_, i) { + traces.each(function (_, i) { var e = d3Select(this); expect(e.select('text').text()).toBe(expectation.items[i]); }); @@ -5279,8 +5819,8 @@ describe('hovermode: (x|y)unified', function() { function assertRectColor(color, bordercolor) { var hover = getHoverLabel(); var bg = hover.select('rect.bg'); - if(color) expect(bg.node().style.fill).toBe(color); - if(bordercolor) expect(bg.node().style.stroke).toBe(bordercolor); + if (color) expect(bg.node().style.fill).toBe(color); + if (bordercolor) expect(bg.node().style.stroke).toBe(bordercolor); } function assertSymbol(exp) { @@ -5288,9 +5828,9 @@ describe('hovermode: (x|y)unified', function() { var traces = hover.selectAll('g.traces'); expect(traces.size()).toBe(exp.length); - traces.each(function(d, i) { + traces.each(function (d, i) { var pts = d3Select(this).selectAll('g.legendpoints path'); - pts.each(function() { + pts.each(function () { var node = d3Select(this).node(); expect(node.style.fill).toBe(exp[i][0], 'wrong fill for point ' + i); expect(node.style.strokeWidth).toBe(exp[i][1], 'wrong stroke-width for point ' + i); @@ -5299,9 +5839,9 @@ describe('hovermode: (x|y)unified', function() { }); } - it('set smart defaults for spikeline in x unified', function(done) { - Plotly.newPlot(gd, [{y: [4, 6, 5]}], {hovermode: 'x unified', xaxis: {color: 'red'}}) - .then(function(gd) { + it('set smart defaults for spikeline in x unified', function (done) { + Plotly.newPlot(gd, [{ y: [4, 6, 5] }], { hovermode: 'x unified', xaxis: { color: 'red' } }) + .then(function (gd) { expect(gd._fullLayout.hovermode).toBe('x unified'); var ax = gd._fullLayout.xaxis; expect(ax.showspike).toBeTrue; @@ -5315,9 +5855,9 @@ describe('hovermode: (x|y)unified', function() { .then(done, done.fail); }); - it('set smart defaults for spikeline in y unified', function(done) { - Plotly.newPlot(gd, [{y: [4, 6, 5]}], {hovermode: 'y unified', yaxis: {color: 'red'}}) - .then(function(gd) { + it('set smart defaults for spikeline in y unified', function (done) { + Plotly.newPlot(gd, [{ y: [4, 6, 5] }], { hovermode: 'y unified', yaxis: { color: 'red' } }) + .then(function (gd) { expect(gd._fullLayout.hovermode).toBe('y unified'); var ax = gd._fullLayout.yaxis; expect(ax.showspike).toBeTrue; @@ -5331,27 +5871,30 @@ describe('hovermode: (x|y)unified', function() { .then(done, done.fail); }); - it('x unified should work for x/y cartesian traces', function(done) { + it('x unified should work for x/y cartesian traces', function (done) { var mockCopy = Lib.extendDeep({}, mock); Plotly.newPlot(gd, mockCopy) - .then(function(gd) { + .then(function (gd) { _hover(gd, { xval: 3 }); - assertLabel({title: '3', items: ['trace 0 : 4', 'trace 1 : 8']}); + assertLabel({ title: '3', items: ['trace 0 : 4', 'trace 1 : 8'] }); }) .then(done, done.fail); }); - it('should not display hover for display: none', function(done) { + it('should not display hover for display: none', function (done) { Plotly.newPlot(gd, { - data: [{ - name: 'A', - y: [1] - }, { - name: 'B', - y: [2], - hoverinfo: 'none' - }], + data: [ + { + name: 'A', + y: [1] + }, + { + name: 'B', + y: [2], + hoverinfo: 'none' + } + ], layout: { hovermode: 'x unified', showlegend: false, @@ -5365,29 +5908,30 @@ describe('hovermode: (x|y)unified', function() { } } }) - .then(function() { - _hover(gd, { xpx: 200, ypx: 200 }); - assertLabel({title: '0', items: [ - 'A : 1' - ]}); - }) - .then(done, done.fail); + .then(function () { + _hover(gd, { xpx: 200, ypx: 200 }); + assertLabel({ title: '0', items: ['A : 1'] }); + }) + .then(done, done.fail); }); - it('should not fail if only hoverinfo: "none" are current visible', function(done) { + it('should not fail if only hoverinfo: "none" are current visible', function (done) { Plotly.newPlot(gd, { - data: [{ - name: 'A', - x: [1, 100], - y: [1, 1] - }, { - name: 'B', - y: [1], - x: [50], - hoverinfo: 'none' - }], + data: [ + { + name: 'A', + x: [1, 100], + y: [1, 1] + }, + { + name: 'B', + y: [1], + x: [50], + hoverinfo: 'none' + } + ], layout: { - xaxis: {range: [40, 60]}, + xaxis: { range: [40, 60] }, hovermode: 'x unified', showlegend: false, width: 500, @@ -5400,27 +5944,27 @@ describe('hovermode: (x|y)unified', function() { } } }) - .then(function() { - _hover(gd, { xpx: 200, ypx: 200 }); - expect(gd._hoverdata, undefined); - assertHoverLabelContent({}); - }) - .then(done, done.fail); + .then(function () { + _hover(gd, { xpx: 200, ypx: 200 }); + expect(gd._hoverdata, undefined); + assertHoverLabelContent({}); + }) + .then(done, done.fail); }); - it('y unified should work for x/y cartesian traces', function(done) { + it('y unified should work for x/y cartesian traces', function (done) { var mockCopy = Lib.extendDeep({}, mock); mockCopy.layout.hovermode = 'y unified'; Plotly.newPlot(gd, mockCopy) - .then(function(gd) { + .then(function (gd) { _hover(gd, { yval: 6 }); - assertLabel({title: '6', items: ['trace 0 : 2', 'trace 1 : 5']}); + assertLabel({ title: '6', items: ['trace 0 : 2', 'trace 1 : 5'] }); }) .then(done, done.fail); }); - it('unified hover modes should work for x/y cartesian traces via template', function(done) { + it('unified hover modes should work for x/y cartesian traces via template', function (done) { var mockCopy = Lib.extendDeep({}, mock); delete mockCopy.layout.hovermode; mockCopy.layout.template = { @@ -5429,7 +5973,7 @@ describe('hovermode: (x|y)unified', function() { } }; Plotly.newPlot(gd, mockCopy) - .then(function(gd) { + .then(function (gd) { expect(gd._fullLayout.hovermode).toBe('y unified'); var ax = gd._fullLayout.yaxis; expect(ax.showspike).toBeTrue; @@ -5442,50 +5986,100 @@ describe('hovermode: (x|y)unified', function() { _hover(gd, { yval: 6 }); - assertLabel({title: '6', items: ['trace 0 : 2', 'trace 1 : 5']}); + assertLabel({ title: '6', items: ['trace 0 : 2', 'trace 1 : 5'] }); }) .then(done, done.fail); }); - it('x unified should work for x/y cartesian traces with legendgroup', function(done) { + it('x unified should work for x/y cartesian traces with legendgroup', function (done) { var mockLegendGroup = require('../../image/mocks/legendgroup.json'); var mockCopy = Lib.extendDeep({}, mockLegendGroup); mockCopy.layout.hovermode = 'x unified'; Plotly.newPlot(gd, mockCopy) - .then(function(gd) { + .then(function (gd) { _hover(gd, { xval: 3 }); - assertLabel({title: '3', items: [ - 'trace 0 : 2', - 'min: 1', - 'lower fence: 1', - 'q1: 1', - 'trace 1 : median: 1', - 'q3: 1', - 'upper fence: 1', - 'max: 1', - 'trace 3 : 2', - 'trace 2 : 2', - 'trace 5 : 2', - 'trace 4 : 1' - ]}); + assertLabel({ + title: '3', + items: [ + 'trace 0 : 2', + 'min: 1', + 'lower fence: 1', + 'q1: 1', + 'trace 1 : median: 1', + 'q3: 1', + 'upper fence: 1', + 'max: 1', + 'trace 3 : 2', + 'trace 2 : 2', + 'trace 5 : 2', + 'trace 4 : 1' + ] + }); + }) + .then(done, done.fail); + }); + + it('should display hover for scatter and bars at various intervals (default hoverdistance)', function (done) { + Plotly.newPlot(gd, { + data: [ + { + name: 'bar', + type: 'bar', + y: [10, 30] + }, + { + name: 'scatter', + type: 'scatter', + x: [0, 0.2, 0.4, 0.6, 0.8, 1], + y: [21, 22, 23, 24, 25, 26] + } + ], + layout: { + hovermode: 'x unified', + showlegend: false, + width: 500, + height: 500, + margin: { + t: 50, + b: 50, + l: 50, + r: 50 + } + } + }) + .then(function () { + _hover(gd, { xpx: 100, ypx: 200 }); + assertLabel({ title: '0', items: ['bar : 10', 'scatter : 21'] }); + }) + .then(function () { + _hover(gd, { xpx: 200, ypx: 200 }); + assertLabel({ title: '0.6', items: ['bar : (1, 30)', 'scatter : 24'] }); + }) + .then(function () { + _hover(gd, { xpx: 300, ypx: 200 }); + assertLabel({ title: '1', items: ['bar : 30', 'scatter : 26'] }); }) .then(done, done.fail); }); - it('should display hover for scatter and bars at various intervals (default hoverdistance)', function(done) { + it('should display hover for scatter and bars at various intervals (case of hoverdistance: -1) tests finitRange', function (done) { Plotly.newPlot(gd, { - data: [{ - name: 'bar', - type: 'bar', - y: [10, 30] - }, { - name: 'scatter', - type: 'scatter', - x: [0, 0.2, 0.4, 0.6, 0.8, 1], - y: [21, 22, 23, 24, 25, 26] - }], + data: [ + { + name: 'bar', + type: 'bar', + y: [10, 30] + }, + { + name: 'scatter', + type: 'scatter', + x: [0, 0.2, 0.4, 0.6, 0.8, 1], + y: [21, 22, 23, 24, 25, 26] + } + ], layout: { + hoverdistance: -1, hovermode: 'x unified', showlegend: false, width: 500, @@ -5498,42 +6092,36 @@ describe('hovermode: (x|y)unified', function() { } } }) - .then(function() { - _hover(gd, { xpx: 100, ypx: 200 }); - assertLabel({title: '0', items: [ - 'bar : 10', - 'scatter : 21' - ]}); - }) - .then(function() { - _hover(gd, { xpx: 200, ypx: 200 }); - assertLabel({title: '0.6', items: [ - 'bar : (1, 30)', - 'scatter : 24' - ]}); - }) - .then(function() { - _hover(gd, { xpx: 300, ypx: 200 }); - assertLabel({title: '1', items: [ - 'bar : 30', - 'scatter : 26' - ]}); - }) - .then(done, done.fail); + .then(function () { + _hover(gd, { xpx: 100, ypx: 200 }); + assertLabel({ title: '0', items: ['bar : 10', 'scatter : 21'] }); + }) + .then(function () { + _hover(gd, { xpx: 200, ypx: 200 }); + assertLabel({ title: '0.6', items: ['bar : (1, 30)', 'scatter : 24'] }); + }) + .then(function () { + _hover(gd, { xpx: 300, ypx: 200 }); + assertLabel({ title: '1', items: ['bar : 30', 'scatter : 26'] }); + }) + .then(done, done.fail); }); - it('should display hover for scatter and bars at various intervals (case of hoverdistance: -1) tests finitRange', function(done) { + it('should pick the bar which is closest to the winning point no the bar that close to the cursor', function (done) { Plotly.newPlot(gd, { - data: [{ - name: 'bar', - type: 'bar', - y: [10, 30] - }, { - name: 'scatter', - type: 'scatter', - x: [0, 0.2, 0.4, 0.6, 0.8, 1], - y: [21, 22, 23, 24, 25, 26] - }], + data: [ + { + name: 'bar', + type: 'bar', + y: [10, 20, 30] + }, + { + name: 'scatter', + type: 'scatter', + x: [0, 0.49, 1, 1.51, 2], + y: [21, 22, 23, 24, 25] + } + ], layout: { hoverdistance: -1, hovermode: 'x unified', @@ -5548,43 +6136,65 @@ describe('hovermode: (x|y)unified', function() { } } }) - .then(function() { - _hover(gd, { xpx: 100, ypx: 200 }); - assertLabel({title: '0', items: [ - 'bar : 10', - 'scatter : 21' - ]}); - }) - .then(function() { - _hover(gd, { xpx: 200, ypx: 200 }); - assertLabel({title: '0.6', items: [ - 'bar : (1, 30)', - 'scatter : 24' - ]}); - }) - .then(function() { - _hover(gd, { xpx: 300, ypx: 200 }); - assertLabel({title: '1', items: [ - 'bar : 30', - 'scatter : 26' - ]}); - }) - .then(done, done.fail); + .then(function () { + _hover(gd, { xpx: 0, ypx: 200 }); + assertLabel({ title: '0', items: ['bar : 10', 'scatter : 21'] }); + + _hover(gd, { xpx: 100, ypx: 200 }); + assertLabel({ title: '0.49', items: ['bar : (0, 10)', 'scatter : 22'] }); + + _hover(gd, { xpx: 150, ypx: 200 }); + assertLabel({ title: '0.49', items: ['bar : (0, 10)', 'scatter : 22'] }); + + _hover(gd, { xpx: 200, ypx: 200 }); + assertLabel({ title: '1', items: ['bar : 20', 'scatter : 23'] }); + + _hover(gd, { xpx: 250, ypx: 200 }); + assertLabel({ title: '1.51', items: ['bar : (2, 30)', 'scatter : 24'] }); + + _hover(gd, { xpx: 300, ypx: 200 }); + assertLabel({ title: '1.51', items: ['bar : (2, 30)', 'scatter : 24'] }); + + _hover(gd, { xpx: 400, ypx: 200 }); + assertLabel({ title: '2', items: ['bar : 30', 'scatter : 25'] }); + }) + .then(done, done.fail); }); - it('should pick the bar which is closest to the winning point no the bar that close to the cursor', function(done) { - Plotly.newPlot(gd, { - data: [{ - name: 'bar', - type: 'bar', - y: [10, 20, 30] - }, { - name: 'scatter', - type: 'scatter', - x: [0, 0.49, 1, 1.51, 2], - y: [21, 22, 23, 24, 25] - }], - layout: { + it('should format differing position using *xother* `hovertemplate` and in respect to `xhoverformat`', function (done) { + Plotly.newPlot( + gd, + [ + { + type: 'bar', + hovertemplate: 'y(_x):%{y}%{_xother:.2f}', + x: [0, 1.001], + y: [2, 1] + }, + { + x: [0, 0.749], + y: [1, 2] + }, + { + hovertemplate: '(x)y:%{xother}%{y}', + xhoverformat: '.1f', + x: [0, 1.251], + y: [2, 3] + }, + { + hovertemplate: '(x_)y:%{xother_}%{y}', + xhoverformat: '.2f', + x: [0, 1.351], + y: [3, 4] + }, + { + hovertemplate: '(_x_)y:%{_xother_}%{y}', + xhoverformat: '.3f', + x: [0, 1.451], + y: [4, 5] + } + ], + { hoverdistance: -1, hovermode: 'x unified', showlegend: false, @@ -5597,124 +6207,50 @@ describe('hovermode: (x|y)unified', function() { r: 50 } } - }) - .then(function() { - _hover(gd, { xpx: 0, ypx: 200 }); - assertLabel({title: '0', items: [ - 'bar : 10', - 'scatter : 21' - ]}); - - _hover(gd, { xpx: 100, ypx: 200 }); - assertLabel({title: '0.49', items: [ - 'bar : (0, 10)', - 'scatter : 22' - ]}); - - _hover(gd, { xpx: 150, ypx: 200 }); - assertLabel({title: '0.49', items: [ - 'bar : (0, 10)', - 'scatter : 22' - ]}); - - _hover(gd, { xpx: 200, ypx: 200 }); - assertLabel({title: '1', items: [ - 'bar : 20', - 'scatter : 23' - ]}); - - _hover(gd, { xpx: 250, ypx: 200 }); - assertLabel({title: '1.51', items: [ - 'bar : (2, 30)', - 'scatter : 24' - ]}); - - _hover(gd, { xpx: 300, ypx: 200 }); - assertLabel({title: '1.51', items: [ - 'bar : (2, 30)', - 'scatter : 24' - ]}); - - _hover(gd, { xpx: 400, ypx: 200 }); - assertLabel({title: '2', items: [ - 'bar : 30', - 'scatter : 25' - ]}); - }) - .then(done, done.fail); - }); - - it('should format differing position using *xother* `hovertemplate` and in respect to `xhoverformat`', function(done) { - Plotly.newPlot(gd, [{ - type: 'bar', - hovertemplate: 'y(_x):%{y}%{_xother:.2f}', - x: [0, 1.001], - y: [2, 1] - }, { - x: [0, 0.749], - y: [1, 2] - }, { - hovertemplate: '(x)y:%{xother}%{y}', - xhoverformat: '.1f', - x: [0, 1.251], - y: [2, 3] - }, { - hovertemplate: '(x_)y:%{xother_}%{y}', - xhoverformat: '.2f', - x: [0, 1.351], - y: [3, 4] - }, { - hovertemplate: '(_x_)y:%{_xother_}%{y}', - xhoverformat: '.3f', - x: [0, 1.451], - y: [4, 5] - }], { - hoverdistance: -1, - hovermode: 'x unified', - showlegend: false, - width: 500, - height: 500, - margin: { - t: 50, - b: 50, - l: 50, - r: 50 - } - }) - .then(function() { - _hover(gd, { xpx: 100, ypx: 200 }); - assertLabel({title: '0.000', items: [ - 'trace 0 : y(_x):2 (0.00)', - 'trace 1 : (0, 1)', - 'trace 2 : (x)y:(0.0)2', - 'trace 3 : (x_)y:(0.00) 3', - 'trace 4 : (_x_)y:4', - ]}); - }) - .then(function() { - _hover(gd, { xpx: 250, ypx: 200 }); - assertLabel({title: '0.749', items: [ - 'trace 0 : y(_x):1 (1.00)', - 'trace 1 : 2', - 'trace 2 : (x)y:(1.3)3', - 'trace 3 : (x_)y:(1.35) 4', - 'trace 4 : (_x_)y: (1.451) 5', - ]}); - }) - .then(function() { - _hover(gd, { xpx: 350, ypx: 200 }); - assertLabel({title: '1.35', items: [ - 'trace 0 : y(_x):1 (1.00)', - 'trace 1 : (0.749, 2)', - 'trace 2 : (x)y:(1.3)3', - 'trace 3 : (x_)y:4', - 'trace 4 : (_x_)y: (1.451) 5', - ]}); - }) - .then(done, done.fail); + ) + .then(function () { + _hover(gd, { xpx: 100, ypx: 200 }); + assertLabel({ + title: '0.000', + items: [ + 'trace 0 : y(_x):2 (0.00)', + 'trace 1 : (0, 1)', + 'trace 2 : (x)y:(0.0)2', + 'trace 3 : (x_)y:(0.00) 3', + 'trace 4 : (_x_)y:4' + ] + }); + }) + .then(function () { + _hover(gd, { xpx: 250, ypx: 200 }); + assertLabel({ + title: '0.749', + items: [ + 'trace 0 : y(_x):1 (1.00)', + 'trace 1 : 2', + 'trace 2 : (x)y:(1.3)3', + 'trace 3 : (x_)y:(1.35) 4', + 'trace 4 : (_x_)y: (1.451) 5' + ] + }); + }) + .then(function () { + _hover(gd, { xpx: 350, ypx: 200 }); + assertLabel({ + title: '1.35', + items: [ + 'trace 0 : y(_x):1 (1.00)', + 'trace 1 : (0.749, 2)', + 'trace 2 : (x)y:(1.3)3', + 'trace 3 : (x_)y:4', + 'trace 4 : (_x_)y: (1.451) 5' + ] + }); + }) + .then(done, done.fail); }); - it('should display hover for two high-res scatter at different various intervals', function(done) { + it('should display hover for two high-res scatter at different various intervals', function (done) { var x1 = []; var y1 = []; var x2 = []; @@ -5725,28 +6261,31 @@ describe('hovermode: (x|y)unified', function() { return Math.round(v * 100); } - for(i = 0; i <= 1800; i++) { - t = i / 180 * Math.PI; + for (i = 0; i <= 1800; i++) { + t = (i / 180) * Math.PI; x1.push(r100(t / 5)); y1.push(r100(Math.sin(t))); } - for(i = 0; i <= 360; i++) { - t = i / 180 * Math.PI; + for (i = 0; i <= 360; i++) { + t = (i / 180) * Math.PI; x2.push(r100(t)); y2.push(r100(Math.sin(t))); } Plotly.newPlot(gd, { - data: [{ - name: 'high', - x: x1, - y: y1 - }, { - name: 'low', - x: x2, - y: y2 - }], + data: [ + { + name: 'high', + x: x1, + y: y1 + }, + { + name: 'low', + x: x2, + y: y2 + } + ], layout: { hovermode: 'x unified', showlegend: false, @@ -5760,42 +6299,28 @@ describe('hovermode: (x|y)unified', function() { } } }) - .then(function() { - _hover(gd, { xpx: 100, ypx: 200 }); - assertLabel({title: '157', items: [ - 'high : 100', - 'low : 100' - ]}); - }) - .then(function() { - _hover(gd, { xpx: 175, ypx: 200 }); - assertLabel({title: '275', items: [ - 'high : 93', - 'low : (274, 39)' - ]}); - }) - .then(function() { - _hover(gd, { xpx: 350, ypx: 200 }); - assertLabel({title: '550', items: [ - 'high : 68', - 'low : −71' - ]}); - }) - .then(done, done.fail); + .then(function () { + _hover(gd, { xpx: 100, ypx: 200 }); + assertLabel({ title: '157', items: ['high : 100', 'low : 100'] }); + }) + .then(function () { + _hover(gd, { xpx: 175, ypx: 200 }); + assertLabel({ title: '275', items: ['high : 93', 'low : (274, 39)'] }); + }) + .then(function () { + _hover(gd, { xpx: 350, ypx: 200 }); + assertLabel({ title: '550', items: ['high : 68', 'low : −71'] }); + }) + .then(done, done.fail); }); - it('case of scatter points on period bars', function(done) { + it('case of scatter points on period bars', function (done) { Plotly.newPlot(gd, { data: [ { type: 'bar', name: 'bar', - x: [ - '2017-04', - '2017-07', - '2017-10', - '2018-01' - ], + x: ['2017-04', '2017-07', '2017-10', '2018-01'], xperiod: 'M3', y: [10, 5, 10, 5] }, @@ -5832,92 +6357,54 @@ describe('hovermode: (x|y)unified', function() { } } }) - .then(function(gd) { - _hover(gd, { xpx: 50, ypx: 250 }); - assertLabel({title: 'Feb 1, 2017', items: [ - 'scatter : 2' - ]}); - - _hover(gd, { xpx: 75, ypx: 250 }); - assertLabel({title: 'Mar 1, 2017', items: [ - 'scatter : 3' - ]}); - - _hover(gd, { xpx: 100, ypx: 250 }); - assertLabel({title: 'Apr 1, 2017', items: [ - 'bar : 10', - 'scatter : 4' - ]}); - - _hover(gd, { xpx: 125, ypx: 250 }); - assertLabel({title: 'May 1, 2017', items: [ - 'bar : (Apr 1, 2017, 10)', - 'scatter : 5' - ]}); - - _hover(gd, { xpx: 150, ypx: 250 }); - assertLabel({title: 'Jun 1, 2017', items: [ - 'bar : (Apr 1, 2017, 10)', - 'scatter : 6' - ]}); - - _hover(gd, { xpx: 175, ypx: 250 }); - assertLabel({title: 'Jul 1, 2017', items: [ - 'bar : 5', - 'scatter : 7' - ]}); - - _hover(gd, { xpx: 200, ypx: 250 }); - assertLabel({title: 'Aug 1, 2017', items: [ - 'bar : (Jul 1, 2017, 5)', - 'scatter : 8' - ]}); - - _hover(gd, { xpx: 225, ypx: 250 }); - assertLabel({title: 'Sep 1, 2017', items: [ - 'bar : (Jul 1, 2017, 5)', - 'scatter : 9' - ]}); - - _hover(gd, { xpx: 250, ypx: 250 }); - assertLabel({title: 'Oct 1, 2017', items: [ - 'bar : 10', - 'scatter : 10' - ]}); - - _hover(gd, { xpx: 275, ypx: 250 }); - assertLabel({title: 'Nov 1, 2017', items: [ - 'bar : (Oct 1, 2017, 10)', - 'scatter : 11' - ]}); - - _hover(gd, { xpx: 300, ypx: 250 }); - assertLabel({title: 'Dec 1, 2017', items: [ - 'bar : (Oct 1, 2017, 10)', - 'scatter : 12' - ]}); - - _hover(gd, { xpx: 350, ypx: 250 }); - assertLabel({title: 'Jan 1, 2018', items: [ - 'bar : 5' - ]}); - }) - .then(done, done.fail); + .then(function (gd) { + _hover(gd, { xpx: 50, ypx: 250 }); + assertLabel({ title: 'Feb 1, 2017', items: ['scatter : 2'] }); + + _hover(gd, { xpx: 75, ypx: 250 }); + assertLabel({ title: 'Mar 1, 2017', items: ['scatter : 3'] }); + + _hover(gd, { xpx: 100, ypx: 250 }); + assertLabel({ title: 'Apr 1, 2017', items: ['bar : 10', 'scatter : 4'] }); + + _hover(gd, { xpx: 125, ypx: 250 }); + assertLabel({ title: 'May 1, 2017', items: ['bar : (Apr 1, 2017, 10)', 'scatter : 5'] }); + + _hover(gd, { xpx: 150, ypx: 250 }); + assertLabel({ title: 'Jun 1, 2017', items: ['bar : (Apr 1, 2017, 10)', 'scatter : 6'] }); + + _hover(gd, { xpx: 175, ypx: 250 }); + assertLabel({ title: 'Jul 1, 2017', items: ['bar : 5', 'scatter : 7'] }); + + _hover(gd, { xpx: 200, ypx: 250 }); + assertLabel({ title: 'Aug 1, 2017', items: ['bar : (Jul 1, 2017, 5)', 'scatter : 8'] }); + + _hover(gd, { xpx: 225, ypx: 250 }); + assertLabel({ title: 'Sep 1, 2017', items: ['bar : (Jul 1, 2017, 5)', 'scatter : 9'] }); + + _hover(gd, { xpx: 250, ypx: 250 }); + assertLabel({ title: 'Oct 1, 2017', items: ['bar : 10', 'scatter : 10'] }); + + _hover(gd, { xpx: 275, ypx: 250 }); + assertLabel({ title: 'Nov 1, 2017', items: ['bar : (Oct 1, 2017, 10)', 'scatter : 11'] }); + + _hover(gd, { xpx: 300, ypx: 250 }); + assertLabel({ title: 'Dec 1, 2017', items: ['bar : (Oct 1, 2017, 10)', 'scatter : 12'] }); + + _hover(gd, { xpx: 350, ypx: 250 }); + assertLabel({ title: 'Jan 1, 2018', items: ['bar : 5'] }); + }) + .then(done, done.fail); }); - it('case of M1 period bars overlaid on M3 period bars', function(done) { + it('case of M1 period bars overlaid on M3 period bars', function (done) { Plotly.newPlot(gd, { data: [ { type: 'bar', name: 'M3', xperiod: 'M3', - x: [ - '2017-04', - '2017-07', - '2017-10', - '2018-01' - ], + x: ['2017-04', '2017-07', '2017-10', '2018-01'], y: [10, 5, 10, 5] }, { @@ -5955,92 +6442,60 @@ describe('hovermode: (x|y)unified', function() { } } }) - .then(function(gd) { - _hover(gd, { xpx: 25, ypx: 250 }); - assertLabel({title: 'Jan 1, 2017', items: [ - 'M1 : 1' - ]}); - - _hover(gd, { xpx: 50, ypx: 250 }); - assertLabel({title: 'Feb 1, 2017', items: [ - 'M1 : 2' - ]}); - - _hover(gd, { xpx: 75, ypx: 250 }); - assertLabel({title: 'Mar 1, 2017', items: [ - 'M1 : 3' - ]}); - - _hover(gd, { xpx: 100, ypx: 250 }); - assertLabel({title: 'Apr 1, 2017', items: [ - 'M3 : 10', - 'M1 : 4' - ]}); - - _hover(gd, { xpx: 125, ypx: 250 }); - assertLabel({title: 'May 1, 2017', items: [ - 'M3 : (Apr 1, 2017, 10)', - 'M1 : 5' - ]}); - - _hover(gd, { xpx: 150, ypx: 250 }); - assertLabel({title: 'Jun 1, 2017', items: [ - 'M3 : (Apr 1, 2017, 10)', - 'M1 : 6' - ]}); - - _hover(gd, { xpx: 175, ypx: 250 }); - assertLabel({title: 'Jul 1, 2017', items: [ - 'M3 : 5', - 'M1 : 7' - ]}); - - _hover(gd, { xpx: 200, ypx: 250 }); - assertLabel({title: 'Aug 1, 2017', items: [ - 'M3 : (Jul 1, 2017, 5)', - 'M1 : 8' - ]}); - - _hover(gd, { xpx: 225, ypx: 250 }); - assertLabel({title: 'Sep 1, 2017', items: [ - 'M3 : (Jul 1, 2017, 5)', - 'M1 : 9' - ]}); - - _hover(gd, { xpx: 250, ypx: 250 }); - assertLabel({title: 'Oct 1, 2017', items: [ - 'M3 : 10', - 'M1 : 10' - ]}); - - _hover(gd, { xpx: 275, ypx: 250 }); - assertLabel({title: 'Nov 1, 2017', items: [ - 'M3 : (Oct 1, 2017, 10)', - 'M1 : 11' - ]}); - - _hover(gd, { xpx: 300, ypx: 250 }); - assertLabel({title: 'Dec 1, 2017', items: [ - 'M3 : (Oct 1, 2017, 10)', - 'M1 : 12' - ]}); - - _hover(gd, { xpx: 350, ypx: 250 }); - assertLabel({title: 'Jan 1, 2018', items: [ - 'M3 : 5' - ]}); - }) - .then(done, done.fail); + .then(function (gd) { + _hover(gd, { xpx: 25, ypx: 250 }); + assertLabel({ title: 'Jan 1, 2017', items: ['M1 : 1'] }); + + _hover(gd, { xpx: 50, ypx: 250 }); + assertLabel({ title: 'Feb 1, 2017', items: ['M1 : 2'] }); + + _hover(gd, { xpx: 75, ypx: 250 }); + assertLabel({ title: 'Mar 1, 2017', items: ['M1 : 3'] }); + + _hover(gd, { xpx: 100, ypx: 250 }); + assertLabel({ title: 'Apr 1, 2017', items: ['M3 : 10', 'M1 : 4'] }); + + _hover(gd, { xpx: 125, ypx: 250 }); + assertLabel({ title: 'May 1, 2017', items: ['M3 : (Apr 1, 2017, 10)', 'M1 : 5'] }); + + _hover(gd, { xpx: 150, ypx: 250 }); + assertLabel({ title: 'Jun 1, 2017', items: ['M3 : (Apr 1, 2017, 10)', 'M1 : 6'] }); + + _hover(gd, { xpx: 175, ypx: 250 }); + assertLabel({ title: 'Jul 1, 2017', items: ['M3 : 5', 'M1 : 7'] }); + + _hover(gd, { xpx: 200, ypx: 250 }); + assertLabel({ title: 'Aug 1, 2017', items: ['M3 : (Jul 1, 2017, 5)', 'M1 : 8'] }); + + _hover(gd, { xpx: 225, ypx: 250 }); + assertLabel({ title: 'Sep 1, 2017', items: ['M3 : (Jul 1, 2017, 5)', 'M1 : 9'] }); + + _hover(gd, { xpx: 250, ypx: 250 }); + assertLabel({ title: 'Oct 1, 2017', items: ['M3 : 10', 'M1 : 10'] }); + + _hover(gd, { xpx: 275, ypx: 250 }); + assertLabel({ title: 'Nov 1, 2017', items: ['M3 : (Oct 1, 2017, 10)', 'M1 : 11'] }); + + _hover(gd, { xpx: 300, ypx: 250 }); + assertLabel({ title: 'Dec 1, 2017', items: ['M3 : (Oct 1, 2017, 10)', 'M1 : 12'] }); + + _hover(gd, { xpx: 350, ypx: 250 }); + assertLabel({ title: 'Jan 1, 2018', items: ['M3 : 5'] }); + }) + .then(done, done.fail); }); - [{ - xperiod: 0, - desc: 'non-period scatter points and period bars' - }, { - xperiod: 5 * 24 * 3600 * 1000, - desc: 'period scatter points and period bars' - }].forEach(function(t) { - it(t.desc, function(done) { + [ + { + xperiod: 0, + desc: 'non-period scatter points and period bars' + }, + { + xperiod: 5 * 24 * 3600 * 1000, + desc: 'period scatter points and period bars' + } + ].forEach(function (t) { + it(t.desc, function (done) { var fig = { data: [ { @@ -6056,15 +6511,26 @@ describe('hovermode: (x|y)unified', function() { name: 'scatter', type: 'scatter', x: [ - '2000-01-01', '2000-01-06', '2000-01-11', '2000-01-16', '2000-01-21', '2000-01-26', - '2000-02-01', '2000-02-06', '2000-02-11', '2000-02-16', '2000-02-21', '2000-02-26', - '2000-03-01', '2000-03-06', '2000-03-11', '2000-03-16', '2000-03-21', '2000-03-26' + '2000-01-01', + '2000-01-06', + '2000-01-11', + '2000-01-16', + '2000-01-21', + '2000-01-26', + '2000-02-01', + '2000-02-06', + '2000-02-11', + '2000-02-16', + '2000-02-21', + '2000-02-26', + '2000-03-01', + '2000-03-06', + '2000-03-11', + '2000-03-16', + '2000-03-21', + '2000-03-26' ], - y: [ - 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, - 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, - 3.1, 3.2, 3.3, 3.4, 3.5, 3.6, - ] + y: [1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 3.1, 3.2, 3.3, 3.4, 3.5, 3.6] } ], layout: { @@ -6076,81 +6542,49 @@ describe('hovermode: (x|y)unified', function() { }; Plotly.newPlot(gd, fig) - .then(function(gd) { - _hover(gd, { xpx: 50, ypx: 200 }); - assertLabel({title: 'Dec', items: [ - 'bar : 2' - ]}); + .then(function (gd) { + _hover(gd, { xpx: 50, ypx: 200 }); + assertLabel({ title: 'Dec', items: ['bar : 2'] }); - _hover(gd, { xpx: 75, ypx: 200 }); - assertLabel({title: 'Dec', items: [ - 'bar : 2' - ]}); + _hover(gd, { xpx: 75, ypx: 200 }); + assertLabel({ title: 'Dec', items: ['bar : 2'] }); - _hover(gd, { xpx: 110, ypx: 200 }); - assertLabel({title: 'Jan 1, 2000', items: [ - 'bar : (Jan, 1)', - 'scatter : 1.1' - ]}); + _hover(gd, { xpx: 110, ypx: 200 }); + assertLabel({ title: 'Jan 1, 2000', items: ['bar : (Jan, 1)', 'scatter : 1.1'] }); - _hover(gd, { xpx: 125, ypx: 200 }); - assertLabel({title: 'Jan 6, 2000', items: [ - 'bar : (Jan, 1)', - 'scatter : 1.2' - ]}); + _hover(gd, { xpx: 125, ypx: 200 }); + assertLabel({ title: 'Jan 6, 2000', items: ['bar : (Jan, 1)', 'scatter : 1.2'] }); - _hover(gd, { xpx: 150, ypx: 200 }); - assertLabel({title: 'Jan 11, 2000', items: [ - 'bar : (Jan, 1)', - 'scatter : 1.3' - ]}); + _hover(gd, { xpx: 150, ypx: 200 }); + assertLabel({ title: 'Jan 11, 2000', items: ['bar : (Jan, 1)', 'scatter : 1.3'] }); - _hover(gd, { xpx: 200, ypx: 200 }); - assertLabel({title: 'Jan 26, 2000', items: [ - 'bar : (Jan, 1)', - 'scatter : 1.6' - ]}); + _hover(gd, { xpx: 200, ypx: 200 }); + assertLabel({ title: 'Jan 26, 2000', items: ['bar : (Jan, 1)', 'scatter : 1.6'] }); - _hover(gd, { xpx: 225, ypx: 200 }); - assertLabel({title: 'Feb 1, 2000', items: [ - 'bar : (Feb, 3)', - 'scatter : 2.1' - ]}); + _hover(gd, { xpx: 225, ypx: 200 }); + assertLabel({ title: 'Feb 1, 2000', items: ['bar : (Feb, 3)', 'scatter : 2.1'] }); - _hover(gd, { xpx: 250, ypx: 200 }); - assertLabel({title: 'Feb 11, 2000', items: [ - 'bar : (Feb, 3)', - 'scatter : 2.3' - ]}); + _hover(gd, { xpx: 250, ypx: 200 }); + assertLabel({ title: 'Feb 11, 2000', items: ['bar : (Feb, 3)', 'scatter : 2.3'] }); - _hover(gd, { xpx: 275, ypx: 200 }); - assertLabel({title: 'Feb 16, 2000', items: [ - 'bar : (Feb, 3)', - 'scatter : 2.4' - ]}); + _hover(gd, { xpx: 275, ypx: 200 }); + assertLabel({ title: 'Feb 16, 2000', items: ['bar : (Feb, 3)', 'scatter : 2.4'] }); - _hover(gd, { xpx: 300, ypx: 200 }); - assertLabel({title: 'Feb 21, 2000', items: [ - 'bar : (Feb, 3)', - 'scatter : 2.5' - ]}); + _hover(gd, { xpx: 300, ypx: 200 }); + assertLabel({ title: 'Feb 21, 2000', items: ['bar : (Feb, 3)', 'scatter : 2.5'] }); - _hover(gd, { xpx: 325, ypx: 200 }); - assertLabel({title: 'Mar 1, 2000', items: [ - 'scatter : 3.1' - ]}); + _hover(gd, { xpx: 325, ypx: 200 }); + assertLabel({ title: 'Mar 1, 2000', items: ['scatter : 3.1'] }); - _hover(gd, { xpx: 350, ypx: 200 }); - assertLabel({title: 'Mar 6, 2000', items: [ - 'scatter : 3.2' - ]}); - }) - .then(done, done.fail); + _hover(gd, { xpx: 350, ypx: 200 }); + assertLabel({ title: 'Mar 6, 2000', items: ['scatter : 3.2'] }); + }) + .then(done, done.fail); }); }); - ['scatter', 'scattergl'].forEach(function(scatterType) { - it(scatterType + ' period points alignments', function(done) { + ['scatter', 'scattergl'].forEach(function (scatterType) { + it(scatterType + ' period points alignments', function (done) { Plotly.newPlot(gd, { data: [ { @@ -6178,7 +6612,7 @@ describe('hovermode: (x|y)unified', function() { xhoverformat: '%b', xperiod: 'M1', xperiodalignment: 'end' - }, + } ], layout: { showlegend: false, @@ -6187,27 +6621,19 @@ describe('hovermode: (x|y)unified', function() { hovermode: 'x unified' } }) - .then(function(gd) { - _hover(gd, { xpx: 50, ypx: 200 }); - assertLabel({title: 'Jan', items: [ - 'bar : 1', - 'start : 1', - 'end : 1', - ]}); + .then(function (gd) { + _hover(gd, { xpx: 50, ypx: 200 }); + assertLabel({ title: 'Jan', items: ['bar : 1', 'start : 1', 'end : 1'] }); - _hover(gd, { xpx: 150, ypx: 200 }); - assertLabel({title: 'Jan', items: [ - 'bar : 1', - 'start : 1', - 'end : 1', - ]}); - }) - .then(done, done.fail); + _hover(gd, { xpx: 150, ypx: 200 }); + assertLabel({ title: 'Jan', items: ['bar : 1', 'start : 1', 'end : 1'] }); + }) + .then(done, done.fail); }); }); - ['scatter', 'scattergl'].forEach(function(scatterType) { - it(scatterType + ' period points alignments (all end edge case)', function(done) { + ['scatter', 'scattergl'].forEach(function (scatterType) { + it(scatterType + ' period points alignments (all end edge case)', function (done) { Plotly.newPlot(gd, { data: [ { @@ -6236,7 +6662,7 @@ describe('hovermode: (x|y)unified', function() { xhoverformat: '%b', xperiod: 'M1', xperiodalignment: 'end' - }, + } ], layout: { showlegend: false, @@ -6245,63 +6671,54 @@ describe('hovermode: (x|y)unified', function() { hovermode: 'x unified' } }) - .then(function(gd) { - _hover(gd, { xpx: 100, ypx: 200 }); - assertLabel({title: 'Jan', items: [ - 'bar : 1', - 'one : 1', - 'two : 1', - ]}); + .then(function (gd) { + _hover(gd, { xpx: 100, ypx: 200 }); + assertLabel({ title: 'Jan', items: ['bar : 1', 'one : 1', 'two : 1'] }); - _hover(gd, { xpx: 300, ypx: 200 }); - assertLabel({title: 'Feb', items: [ - 'bar : 2', - 'one : 2', - 'two : 2', - ]}); - }) - .then(done, done.fail); + _hover(gd, { xpx: 300, ypx: 200 }); + assertLabel({ title: 'Feb', items: ['bar : 2', 'one : 2', 'two : 2'] }); + }) + .then(done, done.fail); }); }); - [{ - type: 'scatter', - alignment: 'start', - x: 350 - }, { - type: 'scatter', - alignment: 'middle', - x: 250 - }, { - type: 'scatter', - alignment: 'end', - x: 150 - }].forEach(function(t) { - it('two ' + t.alignment + ' period positioned ' + t.type + ' points', function(done) { + [ + { + type: 'scatter', + alignment: 'start', + x: 350 + }, + { + type: 'scatter', + alignment: 'middle', + x: 250 + }, + { + type: 'scatter', + alignment: 'end', + x: 150 + } + ].forEach(function (t) { + it('two ' + t.alignment + ' period positioned ' + t.type + ' points', function (done) { var fig = { - data: [{ - x: [ - '1970-01-01', - '1970-07-01', - '1971-01-01' - ], - xperiod: 'M6', - xperiodalignment: t.alignment, - type: t.type, - hovertemplate: '%{y}', - y: [11, 12, 13] - }, { - x: [ - '1970-01-01', - '1970-07-01', - '1971-01-01', - ], - xperiod: 'M6', - xperiodalignment: t.alignment, - type: t.type, - hovertemplate: '%{y}', - y: [1, 2, 3] - }], + data: [ + { + x: ['1970-01-01', '1970-07-01', '1971-01-01'], + xperiod: 'M6', + xperiodalignment: t.alignment, + type: t.type, + hovertemplate: '%{y}', + y: [11, 12, 13] + }, + { + x: ['1970-01-01', '1970-07-01', '1971-01-01'], + xperiod: 'M6', + xperiodalignment: t.alignment, + type: t.type, + hovertemplate: '%{y}', + y: [1, 2, 3] + } + ], layout: { showlegend: false, width: 600, @@ -6311,78 +6728,80 @@ describe('hovermode: (x|y)unified', function() { }; Plotly.newPlot(gd, fig) - .then(function(gd) { - _hover(gd, { xpx: t.x, ypx: 200 }); - assertLabel({title: 'Jul 1, 1970', items: [ - 'trace 0 : 12', - 'trace 1 : 2' - ]}); - }) - .then(done, done.fail); + .then(function (gd) { + _hover(gd, { xpx: t.x, ypx: 200 }); + assertLabel({ title: 'Jul 1, 1970', items: ['trace 0 : 12', 'trace 1 : 2'] }); + }) + .then(done, done.fail); }); - [{ - type: 'bar', - barmode: 'overlay', - alignment: 'start' - }, { - type: 'bar', - barmode: 'group', - alignment: 'middle' - }, { - type: 'bar', - barmode: 'group', - alignment: 'end' - }, { - type: 'bar', - barmode: 'group', - alignment: 'start' - }, { - type: 'bar', - barmode: 'overlay', - alignment: 'middle' - }, { - type: 'bar', - barmode: 'overlay', - alignment: 'end' - }, { - type: 'bar', - barmode: 'stacked', - alignment: 'start' - }, { - type: 'bar', - barmode: 'stacked', - alignment: 'middle' - }, { - type: 'bar', - barmode: 'stacked', - alignment: 'end' - }].forEach(function(t) { - it('two ' + t.alignment + ' period positioned ' + t.barmode + ' bars', function(done) { - var fig = { - data: [{ - x: [ - '1970-01-01', - '1970-07-01', - '1971-01-01' - ], - xperiod: 'M6', - xperiodalignment: t.alignment, - type: t.type, - hovertemplate: '%{y}', - y: [11, 12, 13] - }, { - x: [ - '1970-01-01', - '1970-07-01', - '1971-01-01', - ], - xperiod: 'M6', - xperiodalignment: t.alignment, - type: t.type, - hovertemplate: '%{y}', - y: [1, 2, 3] - }], + [ + { + type: 'bar', + barmode: 'overlay', + alignment: 'start' + }, + { + type: 'bar', + barmode: 'group', + alignment: 'middle' + }, + { + type: 'bar', + barmode: 'group', + alignment: 'end' + }, + { + type: 'bar', + barmode: 'group', + alignment: 'start' + }, + { + type: 'bar', + barmode: 'overlay', + alignment: 'middle' + }, + { + type: 'bar', + barmode: 'overlay', + alignment: 'end' + }, + { + type: 'bar', + barmode: 'stacked', + alignment: 'start' + }, + { + type: 'bar', + barmode: 'stacked', + alignment: 'middle' + }, + { + type: 'bar', + barmode: 'stacked', + alignment: 'end' + } + ].forEach(function (t) { + it('two ' + t.alignment + ' period positioned ' + t.barmode + ' bars', function (done) { + var fig = { + data: [ + { + x: ['1970-01-01', '1970-07-01', '1971-01-01'], + xperiod: 'M6', + xperiodalignment: t.alignment, + type: t.type, + hovertemplate: '%{y}', + y: [11, 12, 13] + }, + { + x: ['1970-01-01', '1970-07-01', '1971-01-01'], + xperiod: 'M6', + xperiodalignment: t.alignment, + type: t.type, + hovertemplate: '%{y}', + y: [1, 2, 3] + } + ], layout: { barmode: t.barmode, showlegend: false, @@ -6393,82 +6812,46 @@ describe('hovermode: (x|y)unified', function() { }; Plotly.newPlot(gd, fig) - .then(function(gd) { - _hover(gd, { xpx: 50, ypx: 200 }); - assertLabel({title: 'Jan 1, 1970', items: [ - 'trace 0 : 11', - 'trace 1 : 1' - ]}); + .then(function (gd) { + _hover(gd, { xpx: 50, ypx: 200 }); + assertLabel({ title: 'Jan 1, 1970', items: ['trace 0 : 11', 'trace 1 : 1'] }); - _hover(gd, { xpx: 100, ypx: 200 }); - assertLabel({title: 'Jan 1, 1970', items: [ - 'trace 0 : 11', - 'trace 1 : 1' - ]}); + _hover(gd, { xpx: 100, ypx: 200 }); + assertLabel({ title: 'Jan 1, 1970', items: ['trace 0 : 11', 'trace 1 : 1'] }); - _hover(gd, { xpx: 150, ypx: 200 }); - assertLabel({title: 'Jul 1, 1970', items: [ - 'trace 0 : 12', - 'trace 1 : 2' - ]}); + _hover(gd, { xpx: 150, ypx: 200 }); + assertLabel({ title: 'Jul 1, 1970', items: ['trace 0 : 12', 'trace 1 : 2'] }); - _hover(gd, { xpx: 200, ypx: 200 }); - assertLabel({title: 'Jul 1, 1970', items: [ - 'trace 0 : 12', - 'trace 1 : 2' - ]}); + _hover(gd, { xpx: 200, ypx: 200 }); + assertLabel({ title: 'Jul 1, 1970', items: ['trace 0 : 12', 'trace 1 : 2'] }); - _hover(gd, { xpx: 250, ypx: 200 }); - assertLabel({title: 'Jul 1, 1970', items: [ - 'trace 0 : 12', - 'trace 1 : 2' - ]}); + _hover(gd, { xpx: 250, ypx: 200 }); + assertLabel({ title: 'Jul 1, 1970', items: ['trace 0 : 12', 'trace 1 : 2'] }); - _hover(gd, { xpx: 300, ypx: 200 }); - assertLabel({title: 'Jan 1, 1971', items: [ - 'trace 0 : 13', - 'trace 1 : 3' - ]}); + _hover(gd, { xpx: 300, ypx: 200 }); + assertLabel({ title: 'Jan 1, 1971', items: ['trace 0 : 13', 'trace 1 : 3'] }); - _hover(gd, { xpx: 350, ypx: 200 }); - assertLabel({title: 'Jan 1, 1971', items: [ - 'trace 0 : 13', - 'trace 1 : 3' - ]}); - - _hover(gd, { xpx: 400, ypx: 200 }); - assertLabel({title: 'Jan 1, 1971', items: [ - 'trace 0 : 13', - 'trace 1 : 3' - ]}); - }) - .then(done, done.fail); + _hover(gd, { xpx: 350, ypx: 200 }); + assertLabel({ title: 'Jan 1, 1971', items: ['trace 0 : 13', 'trace 1 : 3'] }); + + _hover(gd, { xpx: 400, ypx: 200 }); + assertLabel({ title: 'Jan 1, 1971', items: ['trace 0 : 13', 'trace 1 : 3'] }); + }) + .then(done, done.fail); }); }); }); - it('period with hover distance -1 include closest not farthest', function(done) { + it('period with hover distance -1 include closest not farthest', function (done) { Plotly.newPlot(gd, { data: [ { name: 'bar', type: 'bar', - x: [ - '2017-01', - '2017-04', - '2017-07', - '2017-10', - '2018-01', - ], + x: ['2017-01', '2017-04', '2017-07', '2017-10', '2018-01'], xhoverformat: 'Q%q', xperiod: 'M3', - y: [ - 0, - 3, - 6, - 9, - 12 - ] + y: [0, 3, 6, 9, 12] }, { name: 'scatter', @@ -6492,23 +6875,7 @@ describe('hovermode: (x|y)unified', function() { ], xhoverformat: '%b', xperiod: 'M1', - y: [ - 1, - 2, - 3, - 4, - 5, - 6, - 7, - 8, - 9, - 10, - 11, - 12, - 13, - 14, - 15 - ] + y: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15] } ], layout: { @@ -6525,143 +6892,113 @@ describe('hovermode: (x|y)unified', function() { } } }) - .then(function(gd) { - _hover(gd, { xpx: 25, ypx: 200 }); - assertLabel({title: 'Jan', items: [ - 'bar : (Q1, 0)', - 'scatter : 1' - ]}); - - _hover(gd, { xpx: 50, ypx: 200 }); - assertLabel({title: 'Feb', items: [ - 'bar : (Q1, 0)', - 'scatter : 2' - ]}); - - _hover(gd, { xpx: 75, ypx: 200 }); - assertLabel({title: 'Mar', items: [ - 'bar : (Q1, 0)', - 'scatter : 3' - ]}); - - _hover(gd, { xpx: 100, ypx: 200 }); - assertLabel({title: 'Apr', items: [ - 'bar : (Q2, 3)', - 'scatter : 4' - ]}); - - _hover(gd, { xpx: 125, ypx: 200 }); - assertLabel({title: 'May', items: [ - 'bar : (Q2, 3)', - 'scatter : 5' - ]}); - - _hover(gd, { xpx: 150, ypx: 200 }); - assertLabel({title: 'Jun', items: [ - 'bar : (Q2, 3)', - 'scatter : 6' - ]}); - - _hover(gd, { xpx: 200, ypx: 200 }); - assertLabel({title: 'Jul', items: [ - 'bar : (Q3, 6)', - 'scatter : 7' - ]}); - - _hover(gd, { xpx: 225, ypx: 200 }); - assertLabel({title: 'Aug', items: [ - 'bar : (Q3, 6)', - 'scatter : 8' - ]}); - - _hover(gd, { xpx: 250, ypx: 200 }); - assertLabel({title: 'Sep', items: [ - 'bar : (Q3, 6)', - 'scatter : 9' - ]}); - - _hover(gd, { xpx: 275, ypx: 200 }); - assertLabel({title: 'Oct', items: [ - 'bar : (Q4, 9)', - 'scatter : 10' - ]}); - - _hover(gd, { xpx: 300, ypx: 200 }); - assertLabel({title: 'Nov', items: [ - 'bar : (Q4, 9)', - 'scatter : 11' - ]}); - - _hover(gd, { xpx: 325, ypx: 200 }); - assertLabel({title: 'Dec', items: [ - 'bar : (Q4, 9)', - 'scatter : 12' - ]}); - }) - .then(done, done.fail); + .then(function (gd) { + _hover(gd, { xpx: 25, ypx: 200 }); + assertLabel({ title: 'Jan', items: ['bar : (Q1, 0)', 'scatter : 1'] }); + + _hover(gd, { xpx: 50, ypx: 200 }); + assertLabel({ title: 'Feb', items: ['bar : (Q1, 0)', 'scatter : 2'] }); + + _hover(gd, { xpx: 75, ypx: 200 }); + assertLabel({ title: 'Mar', items: ['bar : (Q1, 0)', 'scatter : 3'] }); + + _hover(gd, { xpx: 100, ypx: 200 }); + assertLabel({ title: 'Apr', items: ['bar : (Q2, 3)', 'scatter : 4'] }); + + _hover(gd, { xpx: 125, ypx: 200 }); + assertLabel({ title: 'May', items: ['bar : (Q2, 3)', 'scatter : 5'] }); + + _hover(gd, { xpx: 150, ypx: 200 }); + assertLabel({ title: 'Jun', items: ['bar : (Q2, 3)', 'scatter : 6'] }); + + _hover(gd, { xpx: 200, ypx: 200 }); + assertLabel({ title: 'Jul', items: ['bar : (Q3, 6)', 'scatter : 7'] }); + + _hover(gd, { xpx: 225, ypx: 200 }); + assertLabel({ title: 'Aug', items: ['bar : (Q3, 6)', 'scatter : 8'] }); + + _hover(gd, { xpx: 250, ypx: 200 }); + assertLabel({ title: 'Sep', items: ['bar : (Q3, 6)', 'scatter : 9'] }); + + _hover(gd, { xpx: 275, ypx: 200 }); + assertLabel({ title: 'Oct', items: ['bar : (Q4, 9)', 'scatter : 10'] }); + + _hover(gd, { xpx: 300, ypx: 200 }); + assertLabel({ title: 'Nov', items: ['bar : (Q4, 9)', 'scatter : 11'] }); + + _hover(gd, { xpx: 325, ypx: 200 }); + assertLabel({ title: 'Dec', items: ['bar : (Q4, 9)', 'scatter : 12'] }); + }) + .then(done, done.fail); }); - it('should have the same traceorder as the legend', function(done) { + it('should have the same traceorder as the legend', function (done) { var mock = require('../../image/mocks/stacked_area.json'); var mockCopy = Lib.extendDeep({}, mock); mockCopy.layout.hovermode = 'x unified'; var expectation = ['top : 1', 'middle : 6', 'bottom : 0']; Plotly.newPlot(gd, mockCopy) - .then(function(gd) { + .then(function (gd) { _hover(gd, { xval: 3 }); - assertLabel({title: '3', items: expectation}); + assertLabel({ title: '3', items: expectation }); return Plotly.relayout(gd, 'legend.traceorder', 'normal'); }) - .then(function(gd) { + .then(function (gd) { _hover(gd, { xval: 3 }); - assertLabel({title: '3', items: expectation.reverse()}); + assertLabel({ title: '3', items: expectation.reverse() }); }) .then(done, done.fail); }); - it('should order items based on trace index as in the legend', function(done) { + it('should order items based on trace index as in the legend', function (done) { var mock = require('../../image/mocks/29.json'); var mockCopy = Lib.extendDeep({}, mock); mockCopy.layout.hovermode = 'x unified'; Plotly.newPlot(gd, mockCopy) - .then(function(gd) { - _hover(gd, {curveNumber: 0}); - - assertLabel({title: 'Apr 13, 2014, 15:21:15', items: [ - 'Outdoor (wun... : (Apr 13, 2014, 15:26:12, 69.4)', - '1st Floor (N... : 74.8', - '2nd Floor (R... : (Apr 13, 2014, 15:21:11, 73.625)', - 'Attic (Ardui... : (Apr 13, 2014, 15:26:34, 98.49)' - ]}); + .then(function (gd) { + _hover(gd, { curveNumber: 0 }); + + assertLabel({ + title: 'Apr 13, 2014, 15:21:15', + items: [ + 'Outdoor (wun... : (Apr 13, 2014, 15:26:12, 69.4)', + '1st Floor (N... : 74.8', + '2nd Floor (R... : (Apr 13, 2014, 15:21:11, 73.625)', + 'Attic (Ardui... : (Apr 13, 2014, 15:26:34, 98.49)' + ] + }); }) .then(done, done.fail); }); - it('should work for finance traces', function(done) { + it('should work for finance traces', function (done) { var mockOhlc = require('../../image/mocks/finance_multicategory.json'); var mockCopy = Lib.extendDeep({}, mockOhlc); mockCopy.layout.hovermode = 'x unified'; Plotly.newPlot(gd, mockCopy) - .then(function(gd) { - _hover(gd, {curveNumber: 0, pointNumber: 0}); - - assertLabel({title: 'Group 2 - b', items: [ - 'ohlc : open: 12high: 17low: 9close: 13 ▲', - 'candlestick : open: 22high: 27low: 19close: 23 ▲' - ]}); + .then(function (gd) { + _hover(gd, { curveNumber: 0, pointNumber: 0 }); + + assertLabel({ + title: 'Group 2 - b', + items: [ + 'ohlc : open: 12high: 17low: 9close: 13 ▲', + 'candlestick : open: 22high: 27low: 19close: 23 ▲' + ] + }); }) .then(done, done.fail); }); - it('should work for "legend_horizontal_autowrap"', function(done) { + it('should work for "legend_horizontal_autowrap"', function (done) { var mock = require('../../image/mocks/legend_horizontal_autowrap.json'); var mockCopy = Lib.extendDeep({}, mock); mockCopy.layout.hovermode = 'x unified'; Plotly.newPlot(gd, mockCopy) - .then(function(gd) { - _hover(gd, {xval: 1}); + .then(function (gd) { + _hover(gd, { xval: 1 }); assertElementCount('g.hoverlayer g.legend', 1); assertElementCount('g.hoverlayer g.traces', 20); @@ -6669,93 +7006,84 @@ describe('hovermode: (x|y)unified', function() { .then(done, done.fail); }); - it('should style scatter symbols accordingly', function(done) { + it('should style scatter symbols accordingly', function (done) { var mock = require('../../image/mocks/marker_colorscale_template.json'); var mockCopy = Lib.extendDeep({}, mock); mockCopy.layout.hovermode = 'x unified'; Plotly.newPlot(gd, mockCopy) - .then(function(gd) { - _hover(gd, {xval: 1}); - assertLabel({title: '1', items: ['2']}); + .then(function (gd) { + _hover(gd, { xval: 1 }); + assertLabel({ title: '1', items: ['2'] }); assertSymbol([['rgb(33, 145, 140)', '0px', '']]); }) - .then(function() { - _hover(gd, {xval: 2}); - assertLabel({title: '2', items: ['3']}); + .then(function () { + _hover(gd, { xval: 2 }); + assertLabel({ title: '2', items: ['3'] }); assertSymbol([['rgb(253, 231, 37)', '0px', '']]); }) .then(done, done.fail); }); - it('should style bar symbols accordingly', function(done) { + it('should style bar symbols accordingly', function (done) { var mock = require('../../image/mocks/bar-marker-line-colorscales.json'); var mockCopy = Lib.extendDeep({}, mock); mockCopy.layout.hovermode = 'x unified'; Plotly.newPlot(gd, mockCopy) - .then(function(gd) { - _hover(gd, {xval: 10}); - assertLabel({title: '10', items: ['10']}); + .then(function (gd) { + _hover(gd, { xval: 10 }); + assertLabel({ title: '10', items: ['10'] }); assertSymbol([['rgb(94, 216, 43)', '4px', 'rgb(197, 232, 190)']]); }) - .then(function() { - _hover(gd, {xval: 20}); - assertLabel({title: '20', items: ['20']}); + .then(function () { + _hover(gd, { xval: 20 }); + assertLabel({ title: '20', items: ['20'] }); assertSymbol([['rgb(168, 140, 33)', '4px', 'rgb(111, 193, 115)']]); }) .then(done, done.fail); }); - it('should style funnel symbols accordingly', function(done) { + it('should style funnel symbols accordingly', function (done) { var mock = require('../../image/mocks/funnel_custom.json'); var mockCopy = Lib.extendDeep({}, mock); mockCopy.layout.hovermode = 'x unified'; Plotly.newPlot(gd, mockCopy) - .then(function(gd) { - _hover(gd, {xval: 1}); + .then(function (gd) { + _hover(gd, { xval: 1 }); assertSymbol([ - ['rgb(0, 255, 0)', '0px', ''], - ['rgb(255, 255, 0)', '5px', 'rgb(0, 0, 127)'] + ['rgb(0, 255, 0)', '0px', ''], + ['rgb(255, 255, 0)', '5px', 'rgb(0, 0, 127)'] ]); }) .then(done, done.fail); }); - it('should style waterfall symbols correctly', function(done) { + it('should style waterfall symbols correctly', function (done) { var mock = require('../../image/mocks/waterfall_custom.json'); var mockCopy = Lib.extendDeep({}, mock); mockCopy.layout.hovermode = 'x unified'; Plotly.newPlot(gd, mockCopy) - .then(function(gd) { - _hover(gd, {xval: 4}); - assertSymbol([ - ['rgb(255, 65, 54)', '0px', ''] - ]); + .then(function (gd) { + _hover(gd, { xval: 4 }); + assertSymbol([['rgb(255, 65, 54)', '0px', '']]); return Plotly.restyle(gd, { 'decreasing.marker.line.width': 5, 'decreasing.marker.line.color': 'violet' }); }) - .then(function(gd) { - _hover(gd, {xval: 4}); - assertSymbol([ - ['rgb(255, 65, 54)', '5px', 'rgb(238, 130, 238)'] - ]); + .then(function (gd) { + _hover(gd, { xval: 4 }); + assertSymbol([['rgb(255, 65, 54)', '5px', 'rgb(238, 130, 238)']]); }) .then(done, done.fail); }); - it('label should have bgcolor/bordercolor from hoverlabel or legend or paper_bgcolor', function(done) { + it('label should have bgcolor/bordercolor from hoverlabel or legend or paper_bgcolor', function (done) { var mockCopy = Lib.extendDeep({}, mock); - var bgcolor = [ - 'rgb(10, 10, 10)', - 'rgb(20, 20, 20)', - 'rgb(30, 30, 30)', - 'rgb(40, 40, 40)' - ]; + var bgcolor = ['rgb(10, 10, 10)', 'rgb(20, 20, 20)', 'rgb(30, 30, 30)', 'rgb(40, 40, 40)']; Plotly.newPlot(gd, mockCopy) - .then(function(gd) { + .then(function (gd) { _hover(gd, { xval: 3 }); assertRectColor('rgb(255, 255, 255)', 'rgb(68, 68, 68)'); @@ -6763,7 +7091,7 @@ describe('hovermode: (x|y)unified', function() { // Set paper_bgcolor return Plotly.relayout(gd, 'paper_bgcolor', bgcolor[0]); }) - .then(function(gd) { + .then(function (gd) { _hover(gd, { xval: 3 }); assertRectColor(bgcolor[0]); @@ -6775,7 +7103,7 @@ describe('hovermode: (x|y)unified', function() { 'legend.bordercolor': bgcolor[1] }); }) - .then(function(gd) { + .then(function (gd) { _hover(gd, { xval: 3 }); assertRectColor(bgcolor[1], bgcolor[1]); @@ -6786,7 +7114,7 @@ describe('hovermode: (x|y)unified', function() { 'hoverlabel.bordercolor': bgcolor[2] }); }) - .then(function(gd) { + .then(function (gd) { _hover(gd, { xval: 3 }); assertRectColor(bgcolor[2], bgcolor[2]); @@ -6800,7 +7128,7 @@ describe('hovermode: (x|y)unified', function() { return Plotly.newPlot(gd, mockCopy); }) - .then(function(gd) { + .then(function (gd) { _hover(gd, { xval: 3 }); assertRectColor(bgcolor[1], bgcolor[1]); @@ -6815,7 +7143,7 @@ describe('hovermode: (x|y)unified', function() { return Plotly.newPlot(gd, mockCopy); }) - .then(function(gd) { + .then(function (gd) { _hover(gd, { xval: 3 }); assertRectColor(bgcolor[3], bgcolor[3]); @@ -6823,7 +7151,7 @@ describe('hovermode: (x|y)unified', function() { .then(done, done.fail); }); - it('should use hoverlabel.font or legend.font or layout.font', function(done) { + it('should use hoverlabel.font or legend.font or layout.font', function (done) { function assertFont(fontFamily, fontSize, fontColor) { var hover = getHoverLabel(); var text = hover.select('text.legendtext'); @@ -6838,10 +7166,10 @@ describe('hovermode: (x|y)unified', function() { var mockCopy = Lib.extendDeep({}, mock); // Set layout.font - mockCopy.layout.font = {size: 20, family: 'Mono', color: 'rgb(10, 10, 10)'}; + mockCopy.layout.font = { size: 20, family: 'Mono', color: 'rgb(10, 10, 10)' }; Plotly.newPlot(gd, mockCopy) - .then(function(gd) { - _hover(gd, { xval: 3}); + .then(function (gd) { + _hover(gd, { xval: 3 }); assertFont('Mono', '20px', 'rgb(10, 10, 10)'); @@ -6853,7 +7181,7 @@ describe('hovermode: (x|y)unified', function() { 'legend.font.color': 'rgb(20, 20, 20)' }); }) - .then(function(gd) { + .then(function (gd) { _hover(gd, { xval: 3 }); assertFont('Helvetica', '15px', 'rgb(20, 20, 20)'); @@ -6865,7 +7193,7 @@ describe('hovermode: (x|y)unified', function() { 'hoverlabel.font.color': 'rgb(30, 30, 30)' }); }) - .then(function() { + .then(function () { _hover(gd, { xval: 3 }); assertFont('Arial', '22px', 'rgb(30, 30, 30)'); @@ -6874,12 +7202,12 @@ describe('hovermode: (x|y)unified', function() { delete mockCopy.layout; mockCopy.layout = { hovermode: 'x unified', - template: { layout: { legend: {font: {size: 5, family: 'Mono', color: 'rgb(5, 5, 5)'}}}}, + template: { layout: { legend: { font: { size: 5, family: 'Mono', color: 'rgb(5, 5, 5)' } } } } }; return Plotly.newPlot(gd, mockCopy); }) - .then(function() { + .then(function () { _hover(gd, { xval: 3 }); assertFont('Mono', '5px', 'rgb(5, 5, 5)'); @@ -6888,13 +7216,13 @@ describe('hovermode: (x|y)unified', function() { delete mockCopy.layout; mockCopy.layout = { hovermode: 'x unified', - template: { layout: { hoverlabel: { font: {family: 'Mono', size: 30, color: 'red'}}}}, - legend: {font: {size: 20, family: 'Mono', color: 'rgb(10, 10, 10)'}} + template: { layout: { hoverlabel: { font: { family: 'Mono', size: 30, color: 'red' } } } }, + legend: { font: { size: 20, family: 'Mono', color: 'rgb(10, 10, 10)' } } }; return Plotly.newPlot(gd, mockCopy); }) - .then(function() { + .then(function () { _hover(gd, { xval: 3 }); assertFont('Mono', '30px', 'rgb(255, 0, 0)'); @@ -6902,12 +7230,12 @@ describe('hovermode: (x|y)unified', function() { .then(done, done.fail); }); - it('should use hoverlabel.font for group titles as well as traces', function(done) { + it('should use hoverlabel.font for group titles as well as traces', function (done) { function assertFont(fontFamily, fontSize, fontColor) { var hover = getHoverLabel(); var traces = hover.selectAll('g.traces'); - traces.each(function() { + traces.each(function () { var e = d3Select(this); var text = e.select('text.legendtext'); var node = text.node(); @@ -6922,29 +7250,29 @@ describe('hovermode: (x|y)unified', function() { var mockCopy = Lib.extendDeep({}, groupTitlesMock); mockCopy.layout.hoverlabel = { - font: {size: 20, family: 'Mono', color: 'rgb(255, 127, 0)'} + font: { size: 20, family: 'Mono', color: 'rgb(255, 127, 0)' } }; Plotly.newPlot(gd, mockCopy) - .then(function(gd) { - _hover(gd, { xval: 0}); + .then(function (gd) { + _hover(gd, { xval: 0 }); assertFont('Mono', '20px', 'rgb(255, 127, 0)'); }) .then(done, done.fail); }); - it('should use hoverlabel.grouptitlefont for group titles', function(done) { + it('should use hoverlabel.grouptitlefont for group titles', function (done) { function assertFont(fontFamily, fontSize, fontColor) { var hover = getHoverLabel(); var traces = hover.selectAll('g.traces'); - traces.each(function() { + traces.each(function () { var e = d3Select(this); var text = e.select('text.legendtext'); var node = text.node(); var label = node.innerHTML; - if(label.indexOf('group') !== -1) { + if (label.indexOf('group') !== -1) { var textStyle = window.getComputedStyle(node); expect(textStyle.fontFamily.split(',')[0]).toBe(fontFamily, 'wrong font family'); expect(textStyle.fontSize).toBe(fontSize, 'wrong font size'); @@ -6956,49 +7284,43 @@ describe('hovermode: (x|y)unified', function() { var mockCopy = Lib.extendDeep({}, groupTitlesMock); mockCopy.layout.hoverlabel = { - grouptitlefont: {size: 20, family: 'Mono', color: 'rgb(255, 127, 0)'} + grouptitlefont: { size: 20, family: 'Mono', color: 'rgb(255, 127, 0)' } }; Plotly.newPlot(gd, mockCopy) - .then(function(gd) { - _hover(gd, { xval: 0}); + .then(function (gd) { + _hover(gd, { xval: 0 }); assertFont('Mono', '20px', 'rgb(255, 127, 0)'); }) .then(done, done.fail); }); - it('should work with hovertemplate', function(done) { + it('should work with hovertemplate', function (done) { var mockCopy = Lib.extendDeep({}, mock); mockCopy.data[0].hovertemplate = 'hovertemplate: %{y:0.2f}'; mockCopy.data[1].hovertemplate = 'name%{x:0.2f} %{y:0.2f}'; Plotly.newPlot(gd, mockCopy) - .then(function(gd) { + .then(function (gd) { _hover(gd, { xval: 3 }); - assertLabel({title: '3', items: [ - 'trace 0 : hovertemplate: 4.00', - 'name : 3.00 8.00' - ]}); + assertLabel({ title: '3', items: ['trace 0 : hovertemplate: 4.00', 'name : 3.00 8.00'] }); return Plotly.restyle(gd, 'hovertemplate', '%{y:0.2f}'); }) - .then(function(gd) { + .then(function (gd) { _hover(gd, { xval: 3 }); - assertLabel({title: '3', items: [ - '4.00', - '8.00' - ]}); + assertLabel({ title: '3', items: ['4.00', '8.00'] }); }) .then(done, done.fail); }); - it('on relayout, it deletes existing hover', function(done) { + it('on relayout, it deletes existing hover', function (done) { var mockCopy = Lib.extendDeep({}, mock); mockCopy.layout.hovermode = 'x'; Plotly.newPlot(gd, mockCopy) - .then(function(gd) { + .then(function (gd) { _hover(gd, { xval: 3 }); assertElementCount('g.hovertext', 2); @@ -7006,7 +7328,7 @@ describe('hovermode: (x|y)unified', function() { return Plotly.relayout(gd, 'hovermode', 'x unified'); }) - .then(function(gd) { + .then(function (gd) { _hover(gd, { xval: 3 }); assertElementCount('g.hovertext', 0); @@ -7015,80 +7337,88 @@ describe('hovermode: (x|y)unified', function() { .then(done, done.fail); }); - it('should format title of unified hover in respect to `unifiedhovertitle` linear axis', function(done) { - Plotly.newPlot(gd, [{ - type: 'bar', - y: [1, 2, 3] - }, { - type: 'scatter', - y: [2, 3, 1] - }], { - xaxis: { - unifiedhovertitle: { text: 'X: %{x:.2f}' }, - }, - hovermode: 'x unified', - showlegend: false, - width: 500, - height: 500, - margin: { - t: 50, - b: 50, - l: 50, - r: 50 + it('should format title of unified hover in respect to `unifiedhovertitle` linear axis', function (done) { + Plotly.newPlot( + gd, + [ + { + type: 'bar', + y: [1, 2, 3] + }, + { + type: 'scatter', + y: [2, 3, 1] + } + ], + { + xaxis: { + unifiedhovertitle: { text: 'X: %{x:.2f}' } + }, + hovermode: 'x unified', + showlegend: false, + width: 500, + height: 500, + margin: { + t: 50, + b: 50, + l: 50, + r: 50 + } } - }) - .then(function() { - _hover(gd, { xpx: 200, ypx: 200 }); - assertLabel({title: 'X: 1.00', items: [ - 'trace 0 : 2', - 'trace 1 : 3' - ]}); - }) - .then(done, done.fail); + ) + .then(function () { + _hover(gd, { xpx: 200, ypx: 200 }); + assertLabel({ title: 'X: 1.00', items: ['trace 0 : 2', 'trace 1 : 3'] }); + }) + .then(done, done.fail); }); - it('should format title of unified hover in respect to `unifiedhovertitle` date axis', function(done) { - Plotly.newPlot(gd, [{ - type: 'bar', - x: ['2000-01-01', '2000-02-01', '2000-03-01'], - y: [1, 2, 3] - }, { - type: 'scatter', - x: ['2000-01-01', '2000-02-01', '2000-03-01'], - y: [2, 3, 1] - }], { - xaxis: { - type: 'date', - unifiedhovertitle: { text: 'X: %{x|%x %X}' }, - }, - hovermode: 'x unified', - showlegend: false, - width: 500, - height: 500, - margin: { - t: 50, - b: 50, - l: 50, - r: 50 + it('should format title of unified hover in respect to `unifiedhovertitle` date axis', function (done) { + Plotly.newPlot( + gd, + [ + { + type: 'bar', + x: ['2000-01-01', '2000-02-01', '2000-03-01'], + y: [1, 2, 3] + }, + { + type: 'scatter', + x: ['2000-01-01', '2000-02-01', '2000-03-01'], + y: [2, 3, 1] + } + ], + { + xaxis: { + type: 'date', + unifiedhovertitle: { text: 'X: %{x|%x %X}' } + }, + hovermode: 'x unified', + showlegend: false, + width: 500, + height: 500, + margin: { + t: 50, + b: 50, + l: 50, + r: 50 + } } - }) - .then(function() { - _hover(gd, { xpx: 200, ypx: 200 }); - assertLabel({title: 'X: 02/01/2000 00:00:00', items: [ - 'trace 0 : 2', - 'trace 1 : 3' - ]}); - }) - .then(done, done.fail); + ) + .then(function () { + _hover(gd, { xpx: 200, ypx: 200 }); + assertLabel({ title: 'X: 02/01/2000 00:00:00', items: ['trace 0 : 2', 'trace 1 : 3'] }); + }) + .then(done, done.fail); }); }); -describe('hover on traces with (x|y)hoverformat', function() { +describe('hover on traces with (x|y)hoverformat', function () { 'use strict'; var gd, fig; - beforeEach(function() { + beforeEach(function () { gd = createGraphDiv(); fig = { @@ -7115,11 +7445,12 @@ describe('hover on traces with (x|y)hoverformat', function() { } [ - {type: 'scatter', nums: '(02/01/2000, 1.00)'}, - {type: 'scattergl', nums: '(02/01/2000, 1.00)'}, - {type: 'histogram', nums: '(02/01/2000, 1.00)'}, - {type: 'bar', nums: '(02/01/2000, 1.00)'}, - {type: 'box', + { type: 'scatter', nums: '(02/01/2000, 1.00)' }, + { type: 'scattergl', nums: '(02/01/2000, 1.00)' }, + { type: 'histogram', nums: '(02/01/2000, 1.00)' }, + { type: 'bar', nums: '(02/01/2000, 1.00)' }, + { + type: 'box', name: ['', '', '', '', '', '', ''], nums: [ '(02/01/2000, median: 1.00)', @@ -7131,100 +7462,118 @@ describe('hover on traces with (x|y)hoverformat', function() { '(02/01/2000, min: 1.00)' ] }, - {type: 'ohlc', nums: '02/01/2000\nopen: 4.00\nhigh: 5.00\nlow: 2.00\nclose: 3.00 ▼'}, - {type: 'candlestick', nums: '02/01/2000\nopen: 4.00\nhigh: 5.00\nlow: 2.00\nclose: 3.00 ▼'}, - {type: 'waterfall', nums: '(02/01/2000, 1.00)\n1.00 ▲\nInitial: 0.00'}, - {type: 'funnel', nums: '(02/01/2000, 1.00)\n100% of initial\n100% of previous\n100% of total'}, - ].forEach(function(t) { - it(t.type + ' trace', function(done) { - fig.data = [{ - xhoverformat: '%x', - yhoverformat: '.2f', - x: ['2000-02'], - y: [1], - low: [2], - high: [5], - open: [4], - close: [3] - }]; + { type: 'ohlc', nums: '02/01/2000\nopen: 4.00\nhigh: 5.00\nlow: 2.00\nclose: 3.00 ▼' }, + { type: 'candlestick', nums: '02/01/2000\nopen: 4.00\nhigh: 5.00\nlow: 2.00\nclose: 3.00 ▼' }, + { type: 'waterfall', nums: '(02/01/2000, 1.00)\n1.00 ▲\nInitial: 0.00' }, + { type: 'funnel', nums: '(02/01/2000, 1.00)\n100% of initial\n100% of previous\n100% of total' } + ].forEach(function (t) { + it(t.type + ' trace', function (done) { + fig.data = [ + { + xhoverformat: '%x', + yhoverformat: '.2f', + x: ['2000-02'], + y: [1], + low: [2], + high: [5], + open: [4], + close: [3] + } + ]; fig.data[0].type = t.type; Plotly.newPlot(gd, fig) - .then(function() { _hover(200, 200); }) - .then(function() { - assertHoverLabelContent({ - name: t.name ? t.name : '', - nums: t.nums - }); - }) - .then(done, done.fail); + .then(function () { + _hover(200, 200); + }) + .then(function () { + assertHoverLabelContent({ + name: t.name ? t.name : '', + nums: t.nums + }); + }) + .then(done, done.fail); }); }); [ - {type: 'contour', nums: 'x: 02/01/2000\ny: 1.00\nz: 4.000'}, - {type: 'heatmap', nums: 'x: 02/01/2000\ny: 1.00\nz: 4.000'}, - {type: 'histogram2d', nums: 'x: 02/01/2000\ny: 1.00\nz: 1.000'}, - {type: 'histogram2dcontour', nums: 'x: 01/31/2000\ny: 1.00\nz: 1.000'}, - ].forEach(function(t) { - it(t.type + ' trace', function(done) { - fig.data = [{ - xhoverformat: '%x', - yhoverformat: '.2f', - zhoverformat: '.3f', - x: ['2000-01', '2000-02'], - y: [1, 1], - z: [[1, 2], [3, 4]] - }]; + { type: 'contour', nums: 'x: 02/01/2000\ny: 1.00\nz: 4.000' }, + { type: 'heatmap', nums: 'x: 02/01/2000\ny: 1.00\nz: 4.000' }, + { type: 'histogram2d', nums: 'x: 02/01/2000\ny: 1.00\nz: 1.000' }, + { type: 'histogram2dcontour', nums: 'x: 01/31/2000\ny: 1.00\nz: 1.000' } + ].forEach(function (t) { + it(t.type + ' trace', function (done) { + fig.data = [ + { + xhoverformat: '%x', + yhoverformat: '.2f', + zhoverformat: '.3f', + x: ['2000-01', '2000-02'], + y: [1, 1], + z: [ + [1, 2], + [3, 4] + ] + } + ]; fig.data[0].type = t.type; Plotly.newPlot(gd, fig) - .then(function() { _hover(200, 200); }) - .then(function() { - assertHoverLabelContent({ - name: '', - nums: t.nums - }); - }) - .then(done, done.fail); + .then(function () { + _hover(200, 200); + }) + .then(function () { + assertHoverLabelContent({ + name: '', + nums: t.nums + }); + }) + .then(done, done.fail); }); }); - it('splom trace', function(done) { - fig.data = [{ - xhoverformat: '%x', - yhoverformat: '.2f', - dimensions: [{ - label: 'A', - values: ['2000-01', '2000-02'], - type: 'date' - }, { - label: 'B', - values: [3, 4] - }], - type: 'splom' - }]; + it('splom trace', function (done) { + fig.data = [ + { + xhoverformat: '%x', + yhoverformat: '.2f', + dimensions: [ + { + label: 'A', + values: ['2000-01', '2000-02'], + type: 'date' + }, + { + label: 'B', + values: [3, 4] + } + ], + type: 'splom' + } + ]; Plotly.newPlot(gd, fig) - .then(function() { _hover(180, 220); }) - .then(function() { - assertHoverLabelContent({ - name: '', - nums: '(02/01/2000, 4.00)' - }); - }) - .then(done, done.fail); + .then(function () { + _hover(180, 220); + }) + .then(function () { + assertHoverLabelContent({ + name: '', + nums: '(02/01/2000, 4.00)' + }); + }) + .then(done, done.fail); }); }); -describe('hoverlabel.showarrow', function() { +describe('hoverlabel.showarrow', function () { 'use strict'; var gd; - beforeEach(function() { + beforeEach(function () { gd = createGraphDiv(); }); @@ -7241,85 +7590,106 @@ describe('hoverlabel.showarrow', function() { return hoverLabels.select('path').attr('d'); } - it('should show hover arrow by default', function(done) { - Plotly.newPlot(gd, [{ - x: [1, 2, 3], - y: [1, 2, 1], - type: 'scatter', - mode: 'markers' - }], { - width: 400, - height: 400, - margin: {l: 50, t: 50, r: 50, b: 50} - }) - .then(function() { - _hover(200, 70); // Hover over middle point - }) - .then(delay(HOVERMINTIME * 1.1)) - .then(function() { - var pathD = getHoverPath(); - expect(pathD).not.toBeNull('hover path should exist'); - // Arrow paths contain 'L' commands starting from 0,0 - expect(pathD).toMatch(/^M0,0L/, 'path should contain arrow (L command from 0,0)'); - }) - .then(done, done.fail); + it('should show hover arrow by default', function (done) { + Plotly.newPlot( + gd, + [ + { + x: [1, 2, 3], + y: [1, 2, 1], + type: 'scatter', + mode: 'markers' + } + ], + { + width: 400, + height: 400, + margin: { l: 50, t: 50, r: 50, b: 50 } + } + ) + .then(function () { + _hover(200, 70); // Hover over middle point + }) + .then(delay(HOVERMINTIME * 1.1)) + .then(function () { + var pathD = getHoverPath(); + expect(pathD).not.toBeNull('hover path should exist'); + // Arrow paths contain 'L' commands starting from 0,0 + expect(pathD).toMatch(/^M0,0L/, 'path should contain arrow (L command from 0,0)'); + }) + .then(done, done.fail); }); - it('should hide hover arrow when showarrow is false', function(done) { - Plotly.newPlot(gd, [{ - x: [1, 2, 3], - y: [1, 2, 1], - type: 'scatter', - mode: 'markers' - }], { - width: 400, - height: 400, - margin: {l: 50, t: 50, r: 50, b: 50}, - hoverlabel: { showarrow: false } - }) - .then(function() { - _hover(200, 70); // Hover over middle point - }) - .then(delay(HOVERMINTIME * 1.1)) - .then(function() { - var pathD = getHoverPath(); - expect(pathD).not.toBeNull('hover path should exist'); - // No-arrow paths should be simple rectangles (no 'L' commands starting at 0,0)) - expect(pathD).not.toMatch(/^M0,0L/, 'path should not start at 0,0'); - expect(pathD).toMatch(/^M[\d.-]+,[\d.-]+h/, 'path should start with some numeric point and move horizontally'); - }) - .then(done, done.fail); + it('should hide hover arrow when showarrow is false', function (done) { + Plotly.newPlot( + gd, + [ + { + x: [1, 2, 3], + y: [1, 2, 1], + type: 'scatter', + mode: 'markers' + } + ], + { + width: 400, + height: 400, + margin: { l: 50, t: 50, r: 50, b: 50 }, + hoverlabel: { showarrow: false } + } + ) + .then(function () { + _hover(200, 70); // Hover over middle point + }) + .then(delay(HOVERMINTIME * 1.1)) + .then(function () { + var pathD = getHoverPath(); + expect(pathD).not.toBeNull('hover path should exist'); + // No-arrow paths should be simple rectangles (no 'L' commands starting at 0,0)) + expect(pathD).not.toMatch(/^M0,0L/, 'path should not start at 0,0'); + expect(pathD).toMatch( + /^M[\d.-]+,[\d.-]+h/, + 'path should start with some numeric point and move horizontally' + ); + }) + .then(done, done.fail); }); - it('should work at trace level', function(done) { - Plotly.newPlot(gd, [{ - x: [1, 2, 3], - y: [1, 2, 1], - type: 'scatter', - mode: 'markers', - hoverlabel: { showarrow: false } - }], { - width: 400, - height: 400, - margin: {l: 50, t: 50, r: 50, b: 50} - }) - .then(function() { - _hover(200, 70); // Hover over middle point - }) - .then(delay(HOVERMINTIME * 1.1)) - .then(function() { - var pathD = getHoverPath(); - expect(pathD).not.toBeNull('hover path should exist'); - expect(pathD).not.toMatch(/^M0,0L/, 'trace-level showarrow:false should hide arrow'); - }) - .then(done, done.fail); + it('should work at trace level', function (done) { + Plotly.newPlot( + gd, + [ + { + x: [1, 2, 3], + y: [1, 2, 1], + type: 'scatter', + mode: 'markers', + hoverlabel: { showarrow: false } + } + ], + { + width: 400, + height: 400, + margin: { l: 50, t: 50, r: 50, b: 50 } + } + ) + .then(function () { + _hover(200, 70); // Hover over middle point + }) + .then(delay(HOVERMINTIME * 1.1)) + .then(function () { + var pathD = getHoverPath(); + expect(pathD).not.toBeNull('hover path should exist'); + expect(pathD).not.toMatch(/^M0,0L/, 'trace-level showarrow:false should hide arrow'); + }) + .then(done, done.fail); }); }); -describe('hoversort', function() { +describe('hoversort', function () { var gd; - beforeEach(function() { + beforeEach(function () { gd = createGraphDiv(); }); @@ -7340,23 +7710,23 @@ describe('hoversort', function() { var title = hover.select('text.legendtitletext'); var traces = hover.selectAll('g.traces'); - if(expectation.title) { + if (expectation.title) { expect(title.text()).toBe(expectation.title); } expect(traces.size()).toBe(expectation.items.length, 'has the incorrect number of items'); - traces.each(function(_, i) { + traces.each(function (_, i) { var e = d3Select(this); expect(e.select('text').text()).toBe(expectation.items[i]); }); } - it('should sort unified hover items by value descending', function(done) { + it('should sort unified hover items by value descending', function (done) { Plotly.newPlot(gd, { data: [ - {name: 'small', y: [1, 2, 3]}, - {name: 'large', y: [10, 20, 30]}, - {name: 'medium', y: [5, 10, 15]} + { name: 'small', y: [1, 2, 3] }, + { name: 'large', y: [10, 20, 30] }, + { name: 'medium', y: [5, 10, 15] } ], layout: { hovermode: 'x unified', @@ -7366,23 +7736,19 @@ describe('hoversort', function() { height: 500 } }) - .then(function() { - _hover(gd, { xval: 2 }); - assertLabel({title: '2', items: [ - 'large : 30', - 'medium : 15', - 'small : 3' - ]}); - }) - .then(done, done.fail); + .then(function () { + _hover(gd, { xval: 2 }); + assertLabel({ title: '2', items: ['large : 30', 'medium : 15', 'small : 3'] }); + }) + .then(done, done.fail); }); - it('should sort unified hover items by value ascending', function(done) { + it('should sort unified hover items by value ascending', function (done) { Plotly.newPlot(gd, { data: [ - {name: 'small', y: [1, 2, 3]}, - {name: 'large', y: [10, 20, 30]}, - {name: 'medium', y: [5, 10, 15]} + { name: 'small', y: [1, 2, 3] }, + { name: 'large', y: [10, 20, 30] }, + { name: 'medium', y: [5, 10, 15] } ], layout: { hovermode: 'x unified', @@ -7392,23 +7758,19 @@ describe('hoversort', function() { height: 500 } }) - .then(function() { - _hover(gd, { xval: 2 }); - assertLabel({title: '2', items: [ - 'small : 3', - 'medium : 15', - 'large : 30' - ]}); - }) - .then(done, done.fail); + .then(function () { + _hover(gd, { xval: 2 }); + assertLabel({ title: '2', items: ['small : 3', 'medium : 15', 'large : 30'] }); + }) + .then(done, done.fail); }); - it('should default to trace index order', function(done) { + it('should default to trace index order', function (done) { Plotly.newPlot(gd, { data: [ - {name: 'small', y: [1, 2, 3]}, - {name: 'large', y: [10, 20, 30]}, - {name: 'medium', y: [5, 10, 15]} + { name: 'small', y: [1, 2, 3] }, + { name: 'large', y: [10, 20, 30] }, + { name: 'medium', y: [5, 10, 15] } ], layout: { hovermode: 'x unified', @@ -7417,23 +7779,19 @@ describe('hoversort', function() { height: 500 } }) - .then(function() { - _hover(gd, { xval: 2 }); - assertLabel({title: '2', items: [ - 'small : 3', - 'large : 30', - 'medium : 15' - ]}); - }) - .then(done, done.fail); + .then(function () { + _hover(gd, { xval: 2 }); + assertLabel({ title: '2', items: ['small : 3', 'large : 30', 'medium : 15'] }); + }) + .then(done, done.fail); }); - it('should sort by value descending with bar charts', function(done) { + it('should sort by value descending with bar charts', function (done) { Plotly.newPlot(gd, { data: [ - {name: 'A', type: 'bar', y: [5, 10, 15]}, - {name: 'B', type: 'bar', y: [20, 25, 30]}, - {name: 'C', type: 'bar', y: [1, 2, 3]} + { name: 'A', type: 'bar', y: [5, 10, 15] }, + { name: 'B', type: 'bar', y: [20, 25, 30] }, + { name: 'C', type: 'bar', y: [1, 2, 3] } ], layout: { hovermode: 'x unified', @@ -7443,23 +7801,19 @@ describe('hoversort', function() { height: 500 } }) - .then(function() { - _hover(gd, { xval: 1 }); - assertLabel({title: '1', items: [ - 'B : 25', - 'A : 10', - 'C : 2' - ]}); - }) - .then(done, done.fail); + .then(function () { + _hover(gd, { xval: 1 }); + assertLabel({ title: '1', items: ['B : 25', 'A : 10', 'C : 2'] }); + }) + .then(done, done.fail); }); - it('should sort by value descending with y unified hovermode', function(done) { + it('should sort by value descending with y unified hovermode', function (done) { Plotly.newPlot(gd, { data: [ - {name: 'first', x: [1, 10, 5]}, - {name: 'second', x: [20, 2, 15]}, - {name: 'third', x: [8, 8, 8]} + { name: 'first', x: [1, 10, 5] }, + { name: 'second', x: [20, 2, 15] }, + { name: 'third', x: [8, 8, 8] } ], layout: { hovermode: 'y unified', @@ -7469,23 +7823,19 @@ describe('hoversort', function() { height: 500 } }) - .then(function() { - _hover(gd, { yval: 0 }); - assertLabel({title: '0', items: [ - 'second : 20', - 'third : 8', - 'first : 1' - ]}); - }) - .then(done, done.fail); + .then(function () { + _hover(gd, { yval: 0 }); + assertLabel({ title: '0', items: ['second : 20', 'third : 8', 'first : 1'] }); + }) + .then(done, done.fail); }); - it('should fall back to trace index when values are equal', function(done) { + it('should fall back to trace index when values are equal', function (done) { Plotly.newPlot(gd, { data: [ - {name: 'A', y: [5, 10, 10]}, - {name: 'B', y: [5, 20, 10]}, - {name: 'C', y: [5, 5, 10]} + { name: 'A', y: [5, 10, 10] }, + { name: 'B', y: [5, 20, 10] }, + { name: 'C', y: [5, 5, 10] } ], layout: { hovermode: 'x unified', @@ -7495,23 +7845,19 @@ describe('hoversort', function() { height: 500 } }) - .then(function() { - // At xval=0, all values are 5 - should keep trace order - _hover(gd, { xval: 0 }); - assertLabel({title: '0', items: [ - 'A : 5', - 'B : 5', - 'C : 5' - ]}); - }) - .then(done, done.fail); + .then(function () { + // At xval=0, all values are 5 - should keep trace order + _hover(gd, { xval: 0 }); + assertLabel({ title: '0', items: ['A : 5', 'B : 5', 'C : 5'] }); + }) + .then(done, done.fail); }); - it('should dynamically update sort via relayout', function(done) { + it('should dynamically update sort via relayout', function (done) { Plotly.newPlot(gd, { data: [ - {name: 'low', y: [1, 2, 3]}, - {name: 'high', y: [10, 20, 30]} + { name: 'low', y: [1, 2, 3] }, + { name: 'high', y: [10, 20, 30] } ], layout: { hovermode: 'x unified', @@ -7521,22 +7867,16 @@ describe('hoversort', function() { height: 500 } }) - .then(function() { - _hover(gd, { xval: 1 }); - assertLabel({title: '1', items: [ - 'low : 2', - 'high : 20' - ]}); - - return Plotly.relayout(gd, 'hoversort', 'value descending'); - }) - .then(function() { - _hover(gd, { xval: 1 }); - assertLabel({title: '1', items: [ - 'high : 20', - 'low : 2' - ]}); - }) - .then(done, done.fail); + .then(function () { + _hover(gd, { xval: 1 }); + assertLabel({ title: '1', items: ['low : 2', 'high : 20'] }); + + return Plotly.relayout(gd, 'hoversort', 'value descending'); + }) + .then(function () { + _hover(gd, { xval: 1 }); + assertLabel({ title: '1', items: ['high : 20', 'low : 2'] }); + }) + .then(done, done.fail); }); }); From 1400e7cdaf9bc464d4141cacc4b972f67621b7c2 Mon Sep 17 00:00:00 2001 From: Cameron DeCoster Date: Tue, 30 Jun 2026 15:59:15 -0600 Subject: [PATCH 2/6] fix: Populate xval/yval from calcdata when array-mode selection uses pointNumber --- src/components/fx/hover.js | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/components/fx/hover.js b/src/components/fx/hover.js index 20715d2dda4..543a4606f46 100644 --- a/src/components/fx/hover.js +++ b/src/components/fx/hover.js @@ -580,7 +580,24 @@ function _hover(gd, evt, subplot, noHoverEvent, eventTarget) { if (_mode === 'array') { var selection = evt[curvenum]; if ('pointNumber' in selection) { - pointData.index = selection.pointNumber; + // populate xval/yval from the targeted calcdata point so trace + // hoverPoints implementations that read them outside getClosest + // (e.g. scattermap's longitude winding) get valid coordinates + const cdi = cd[selection.pointNumber]; + if (cdi) { + pointData.index = selection.pointNumber; + // map and geo traces store coords as lonlat: [lon, lat]; + // everything else uses Cartesian .x/.y. This is good enough + // for now, but may need to be updated to a per trace accessor + // in the future. + if (cdi.lonlat) { + xval = cdi.lonlat[0]; + yval = cdi.lonlat[1]; + } else { + xval = cdi.x; + yval = cdi.y; + } + } _mode = 'closest'; } else { _mode = ''; From 506fa5e029b4e064b2b1e5b0f0996cc505a4bead Mon Sep 17 00:00:00 2001 From: Cameron DeCoster Date: Tue, 30 Jun 2026 15:59:20 -0600 Subject: [PATCH 3/6] Add test --- test/jasmine/tests/hover_label_test.js | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/test/jasmine/tests/hover_label_test.js b/test/jasmine/tests/hover_label_test.js index f8dca447bc1..2076c3fcf41 100644 --- a/test/jasmine/tests/hover_label_test.js +++ b/test/jasmine/tests/hover_label_test.js @@ -76,6 +76,31 @@ describe('Fx.hover:', function () { }) .then(done, done.fail); }); + + it('populates xval/yval from calcdata when called with a pointNumber selection', (done) => { + // regression: array-mode dispatcher used to leave xval/yval undefined when + // the selection carried pointNumber, which crashed scattermap (NaN lonShift) + const data = [{ x: [10, 20, 30], y: [1, 2, 3] }]; + + Plotly.newPlot(gd, data) + .then(() => { + const traceModule = gd._fullData[0]._module; + let capturedArgs; + const originalHoverPoints = traceModule.hoverPoints; + spyOn(traceModule, 'hoverPoints').and.callFake((...args) => { + capturedArgs = args; + return originalHoverPoints(...args); + }); + + Plotly.Fx.hover(gd, [{ curveNumber: 0, pointNumber: 1 }]); + + expect(traceModule.hoverPoints).toHaveBeenCalled(); + // signature: hoverPoints(pointData, xval, yval, hovermode, opts) + expect(capturedArgs[1]).toBe(20, 'xval should match cd[1].x'); + expect(capturedArgs[2]).toBe(2, 'yval should match cd[1].y'); + }) + .then(done, done.fail); + }); }); describe('hover info', function () { From 475f6a1ca22e36e7594b4b17a93f2b717227d7b2 Mon Sep 17 00:00:00 2001 From: Cameron DeCoster Date: Tue, 30 Jun 2026 16:25:07 -0600 Subject: [PATCH 4/6] Add draftlog --- draftlogs/7882_fix.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 draftlogs/7882_fix.md diff --git a/draftlogs/7882_fix.md b/draftlogs/7882_fix.md new file mode 100644 index 00000000000..5d5b70e862e --- /dev/null +++ b/draftlogs/7882_fix.md @@ -0,0 +1 @@ + - Fix `Plotly.Fx.hover` crash on `scattermap` traces when called programmatically with a `pointNumber` selection [[#7702](https://github.com/plotly/plotly.js/issues/7702)] From 9a5a09452cd84e50e9647b3c907f4267b0ff3624 Mon Sep 17 00:00:00 2001 From: Cameron DeCoster Date: Tue, 30 Jun 2026 16:26:49 -0600 Subject: [PATCH 5/6] Fix draftlog link --- draftlogs/7882_fix.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/draftlogs/7882_fix.md b/draftlogs/7882_fix.md index 5d5b70e862e..73ad2dc3a06 100644 --- a/draftlogs/7882_fix.md +++ b/draftlogs/7882_fix.md @@ -1 +1 @@ - - Fix `Plotly.Fx.hover` crash on `scattermap` traces when called programmatically with a `pointNumber` selection [[#7702](https://github.com/plotly/plotly.js/issues/7702)] + - Fix `Plotly.Fx.hover` crash on `scattermap` traces when called programmatically with a `pointNumber` selection [[#7882](https://github.com/plotly/plotly.js/issues/7882)] From 1e16d9cd306b36886421805942e8e41f5245725d Mon Sep 17 00:00:00 2001 From: Cameron DeCoster Date: Tue, 30 Jun 2026 16:28:06 -0600 Subject: [PATCH 6/6] Actually fix link --- draftlogs/7882_fix.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/draftlogs/7882_fix.md b/draftlogs/7882_fix.md index 73ad2dc3a06..7993e353437 100644 --- a/draftlogs/7882_fix.md +++ b/draftlogs/7882_fix.md @@ -1 +1 @@ - - Fix `Plotly.Fx.hover` crash on `scattermap` traces when called programmatically with a `pointNumber` selection [[#7882](https://github.com/plotly/plotly.js/issues/7882)] + - Fix `Plotly.Fx.hover` crash on `scattermap` traces when called programmatically with a `pointNumber` selection [[#7882](https://github.com/plotly/plotly.js/pull/7882)]