diff --git a/src/traces/sunburst/calc.js b/src/traces/sunburst/calc.js index 04d8a196106..703dbe8c79a 100644 --- a/src/traces/sunburst/calc.js +++ b/src/traces/sunburst/calc.js @@ -128,7 +128,8 @@ exports.calc = function(gd, trace) { cd.unshift({ hasMultipleRoots: true, id: dummyId, - pid: '' + pid: '', + label: '' }); } diff --git a/src/traces/sunburst/fx.js b/src/traces/sunburst/fx.js index 25f4eceb1a1..5efae33736f 100644 --- a/src/traces/sunburst/fx.js +++ b/src/traces/sunburst/fx.js @@ -24,11 +24,6 @@ module.exports = function attachFxHandlers(sliceTop, entry, gd, cd, opts) { var cd0 = cd[0]; var trace = cd0.trace; var hierarchy = cd0.hierarchy; - var rootLabel = hierarchy.data.data.pid; - var readLabel = function(refPt) { - var l = helpers.getPtLabel(refPt); - return l === undefined ? rootLabel : l; - }; var isSunburst = trace.type === 'sunburst'; var isTreemap = trace.type === 'treemap'; @@ -53,7 +48,7 @@ module.exports = function attachFxHandlers(sliceTop, entry, gd, cd, opts) { var isRoot = helpers.isHierarchyRoot(pt); var parent = helpers.getParent(hierarchy, pt); - var val = helpers.getVal(pt); + var val = helpers.getValue(pt); var _cast = function(astr) { return Lib.castOption(traceNow, ptNumber, astr); @@ -109,24 +104,22 @@ module.exports = function attachFxHandlers(sliceTop, entry, gd, cd, opts) { } }; - if(parent) { - hoverPt.percentParent = pt.percentParent = val / helpers.getVal(parent); - hoverPt.parent = pt.parentString = readLabel(parent); - if(hasFlag('percent parent')) { - tx = helpers.formatPercent(hoverPt.percentParent, separators) + ' of ' + hoverPt.parent; - insertPercent(); - } + hoverPt.percentParent = pt.percentParent = val / helpers.getValue(parent); + hoverPt.parent = pt.parentString = helpers.getPtLabel(parent); + if(hasFlag('percent parent')) { + tx = helpers.formatPercent(hoverPt.percentParent, separators) + ' of ' + hoverPt.parent; + insertPercent(); } - hoverPt.percentEntry = pt.percentEntry = val / helpers.getVal(entry); - hoverPt.entry = pt.entry = readLabel(entry); + hoverPt.percentEntry = pt.percentEntry = val / helpers.getValue(entry); + hoverPt.entry = pt.entry = helpers.getPtLabel(entry); if(hasFlag('percent entry') && !isRoot && !pt.onPathbar) { tx = helpers.formatPercent(hoverPt.percentEntry, separators) + ' of ' + hoverPt.entry; insertPercent(); } - hoverPt.percentRoot = pt.percentRoot = val / helpers.getVal(hierarchy); - hoverPt.root = pt.root = readLabel(hierarchy); + hoverPt.percentRoot = pt.percentRoot = val / helpers.getValue(hierarchy); + hoverPt.root = pt.root = helpers.getPtLabel(hierarchy); if(hasFlag('percent root') && !isRoot) { tx = helpers.formatPercent(hoverPt.percentRoot, separators) + ' of ' + hoverPt.root; insertPercent(); @@ -307,7 +300,7 @@ function makeEventData(pt, trace, keys) { if(key in pt) out[key] = pt[key]; } // handle special case of parent - if('parentString' in pt) out.parent = pt.parentString; + if('parentString' in pt && !helpers.isHierarchyRoot(pt)) out.parent = pt.parentString; appendArrayPointValue(out, trace, cdi.i); diff --git a/src/traces/sunburst/helpers.js b/src/traces/sunburst/helpers.js index 269dfcbbeec..d16ddca82c4 100644 --- a/src/traces/sunburst/helpers.js +++ b/src/traces/sunburst/helpers.js @@ -55,7 +55,7 @@ exports.getPtLabel = function(pt) { return pt.data.data.label; }; -exports.getVal = function(d) { +exports.getValue = function(d) { return d.value; }; @@ -152,10 +152,7 @@ function getParentId(pt) { } exports.getParent = function(hierarchy, pt) { - var parentId = getParentId(pt); - return parentId === '' ? - undefined : - exports.findEntryWithLevel(hierarchy, parentId); + return exports.findEntryWithLevel(hierarchy, getParentId(pt)); }; exports.listPath = function(d, keyStr) { diff --git a/src/traces/sunburst/plot.js b/src/traces/sunburst/plot.js index 89028f8bbe7..29ca35091b4 100644 --- a/src/traces/sunburst/plot.js +++ b/src/traces/sunburst/plot.js @@ -488,15 +488,9 @@ exports.formatSliceLabel = function(pt, entry, trace, cd, fullLayout) { var cd0 = cd[0]; var cdi = pt.data.data; var hierarchy = cd0.hierarchy; - var rootLabel = hierarchy.data.data.pid; - var readLabel = function(refPt) { - var l = helpers.getPtLabel(refPt); - return l === undefined ? rootLabel : l; - }; - var isRoot = helpers.isHierarchyRoot(pt); var parent = helpers.getParent(hierarchy, pt); - var val = helpers.getVal(pt); + var val = helpers.getValue(pt); if(!texttemplate) { var parts = textinfo.split('+'); @@ -532,16 +526,16 @@ exports.formatSliceLabel = function(pt, entry, trace, cd, fullLayout) { thisText.push(tx); }; - if(hasFlag('percent parent') && parent) { - percent = val / helpers.getVal(parent); + if(hasFlag('percent parent') && !isRoot) { + percent = val / helpers.getValue(parent); addPercent('parent'); } if(hasFlag('percent entry')) { - percent = val / helpers.getVal(entry); + percent = val / helpers.getValue(entry); addPercent('entry'); } if(hasFlag('percent root')) { - percent = val / helpers.getVal(hierarchy); + percent = val / helpers.getValue(hierarchy); addPercent('root'); } } @@ -566,25 +560,25 @@ exports.formatSliceLabel = function(pt, entry, trace, cd, fullLayout) { obj.currentPath = helpers.getPath(pt.data); - if(parent) { - obj.percentParent = val / helpers.getVal(parent); + if(!isRoot) { + obj.percentParent = val / helpers.getValue(parent); obj.percentParentLabel = helpers.formatPercent( obj.percentParent, separators ); - obj.parent = readLabel(parent); + obj.parent = helpers.getPtLabel(parent); } - obj.percentEntry = val / helpers.getVal(entry); + obj.percentEntry = val / helpers.getValue(entry); obj.percentEntryLabel = helpers.formatPercent( obj.percentEntry, separators ); - obj.entry = readLabel(entry); + obj.entry = helpers.getPtLabel(entry); - obj.percentRoot = val / helpers.getVal(hierarchy); + obj.percentRoot = val / helpers.getValue(hierarchy); obj.percentRootLabel = helpers.formatPercent( obj.percentRoot, separators ); - obj.root = readLabel(hierarchy); + obj.root = helpers.getPtLabel(hierarchy); if(cdi.hasOwnProperty('color')) { obj.color = cdi.color; diff --git a/test/image/baselines/treemap_textposition.png b/test/image/baselines/treemap_textposition.png index 7ecbc57290f..25c4daf1137 100644 Binary files a/test/image/baselines/treemap_textposition.png and b/test/image/baselines/treemap_textposition.png differ diff --git a/test/jasmine/tests/sunburst_test.js b/test/jasmine/tests/sunburst_test.js index 15afa342343..ed3524e6719 100644 --- a/test/jasmine/tests/sunburst_test.js +++ b/test/jasmine/tests/sunburst_test.js @@ -235,7 +235,7 @@ describe('Test sunburst calc:', function() { expect(extract('id')).toEqual(['dummy', 'A', 'B', 'b']); expect(extract('pid')).toEqual(['', 'dummy', 'dummy', 'B']); - expect(extract('label')).toEqual([undefined, 'A', 'B', 'b']); + expect(extract('label')).toEqual(['', 'A', 'B', 'b']); }); it('should compute hierarchy values', function() { diff --git a/test/jasmine/tests/treemap_test.js b/test/jasmine/tests/treemap_test.js index b812e58b282..71158659648 100644 --- a/test/jasmine/tests/treemap_test.js +++ b/test/jasmine/tests/treemap_test.js @@ -346,7 +346,7 @@ describe('Test treemap calc:', function() { expect(extract('id')).toEqual(['dummy', 'A', 'B', 'b']); expect(extract('pid')).toEqual(['', 'dummy', 'dummy', 'B']); - expect(extract('label')).toEqual([undefined, 'A', 'B', 'b']); + expect(extract('label')).toEqual(['', 'A', 'B', 'b']); }); it('should compute hierarchy values', function() { @@ -629,6 +629,148 @@ describe('Test treemap hover:', function() { }); }); +describe('Test treemap hover with and without levels', function() { + var gd; + + var labels0 = ['Alpha', 'Bravo', 'Charlie', 'Delta', 'Echo', 'Foxtrot', 'Golf', 'Hotel', 'India', 'Juliet', 'Kilo', 'Lima', 'Mike', 'November', 'Oscar', 'Papa', 'Quebec', 'Romeo', 'Sierra', 'Tango', 'Uniform', 'Victor', 'Whiskey', 'X ray', 'Yankee', 'Zulu']; + var parents0 = ['', 'Alpha', 'Alpha', 'Charlie', 'Charlie', 'Charlie', 'Foxtrot', 'Foxtrot', 'Foxtrot', 'Foxtrot', 'Juliet', 'Juliet', 'Juliet', 'Juliet', 'Juliet', 'Oscar', 'Oscar', 'Oscar', 'Oscar', 'Oscar', 'Oscar', 'Uniform', 'Uniform', 'Uniform', 'Uniform', 'Uniform', 'Uniform']; + var values0 = [40, 2, 38, 1.5, 2.5, 34, 1, 2, 3, 28, 1.25, 1.75, 2.25, 2.75, 20, 1, 1.5, 2, 2.5, 3, 10, 1, 1.5, 2, 2.5, 3]; + var text0 = ['forty', 'two', 'thirty-eight', 'one and a half', 'two and a half', 'thirty-four', 'one', 'two', 'three', 'twenty-eight', 'one and twenty-five hundredths', 'one and seventy-five hundredths', 'two and twenty-five hundredths', 'two and seventy-five hundredths', 'twenty', 'one', 'one and a half', 'two', 'two and a half', 'three', 'ten', 'one', 'one and a half', 'two', 'two and a half', 'three']; + + afterEach(destroyGraphDiv); + + function run(spec) { + gd = createGraphDiv(); + + var data = (spec.traces || [{}]).map(function(t) { + t.type = 'treemap'; + t.text = text0; + t.values = values0; + t.level = spec.level; + t.branchvalues = 'total'; + t.hovertemplate = 'path = %{currentPath}
label = %{label}
text = %{text}
value = %{value}
ratio to %{parent} = %{percentParent}
ratio to %{entry} = %{percentEntry}
ratio to %{root} = %{percentRoot}'; + + if(!t.labels) t.labels = labels0.slice(); + if(!t.parents) t.parents = parents0.slice(); + return t; + }); + + var layout = Lib.extendFlat({ + width: 500, + height: 500, + margin: {t: 0, b: 0, l: 0, r: 0, pad: 0} + }, spec.layout || {}); + + var exp = spec.exp || {}; + var ptData = null; + + return Plotly.plot(gd, data, layout) + .then(function() { + gd.once('plotly_hover', function(d) { ptData = d.points[0]; }); + }) + .then(hover(gd, spec.pos)) + .then(function() { + assertHoverLabelContent(exp.label); + + for(var k in exp.ptData) { + expect(ptData[k]).toBe(exp.ptData[k], 'pt event data key ' + k); + } + + if(exp.style) { + var gd3 = d3.select(gd); + assertHoverLabelStyle(gd3.select('.hovertext'), exp.style); + } + }); + } + + [{ + desc: 'entry', + level: 'X ray', + pos: 0, + exp: { + label: { + name: 'trace 0', + nums: 'path = Alpha/Charlie/Foxtrot/Juliet/Oscar/Uniform/\nlabel = X ray\ntext = two\nvalue = 2\nratio to Uniform = 0.2\nratio to X ray = 1\nratio to Alpha = 0.05', + }, + ptData: { + curveNumber: 0, + pointNumber: 23, + label: 'X ray', + parent: 'Uniform' + } + } + }, { + desc: 'entry', + level: 'Oscar', + pos: 0, + exp: { + label: { + name: 'trace 0', + nums: 'path = Alpha/Charlie/Foxtrot/Juliet/\nlabel = Oscar\ntext = twenty\nvalue = 20\nratio to Juliet = 0.7142857142857143\nratio to Oscar = 1\nratio to Alpha = 0.5', + }, + ptData: { + curveNumber: 0, + pointNumber: 14, + label: 'Oscar', + parent: 'Juliet' + } + } + }, { + desc: 'leaf', + level: 'Oscar', + pos: 10, + exp: { + label: { + name: 'trace 0', + nums: 'path = Alpha/Charlie/Foxtrot/Juliet/Oscar/Uniform/\nlabel = X ray\ntext = two\nvalue = 2\nratio to Uniform = 0.2\nratio to Oscar = 0.1\nratio to Alpha = 0.05', + }, + ptData: { + curveNumber: 0, + pointNumber: 23, + label: 'X ray', + parent: 'Uniform' + } + } + }, { + desc: 'entry', + level: undefined, // root + pos: 0, + exp: { + label: { + name: 'trace 0', + nums: 'path = /\nlabel = Alpha\ntext = forty\nvalue = 40\nratio to = 1\nratio to Alpha = 1\nratio to Alpha = 1', + }, + ptData: { + curveNumber: 0, + pointNumber: 0, + label: 'Alpha', + parent: '' + } + } + }, { + desc: 'leaf', + level: undefined, // root + pos: 10, + exp: { + label: { + name: 'trace 0', + nums: 'path = Alpha/Charlie/Foxtrot/\nlabel = Golf\ntext = one\nvalue = 1\nratio to Foxtrot = 0.029411764705882353\nratio to Alpha = 0.025\nratio to Alpha = 0.025', + }, + ptData: { + curveNumber: 0, + pointNumber: 6, + label: 'Golf', + parent: 'Foxtrot' + } + } + }] + .forEach(function(spec) { + it('should generate correct hover labels and event data - ' + spec.desc + ' with level:' + spec.level, function(done) { + run(spec).catch(failTest).then(done); + }); + }); +}); + describe('Test treemap hover lifecycle:', function() { var gd; var hoverData;