diff --git a/src/components/legend/draw.js b/src/components/legend/draw.js index 17d386288fb..4a81fb12f1d 100644 --- a/src/components/legend/draw.js +++ b/src/components/legend/draw.js @@ -62,14 +62,14 @@ module.exports = function draw(gd) { var bg = legend.selectAll('rect.bg') .data([0]); - bg.enter().append('rect') - .attr({ - 'class': 'bg', - 'shape-rendering': 'crispEdges' - }) - .call(Color.stroke, opts.bordercolor) - .call(Color.fill, opts.bgcolor) - .style('stroke-width', opts.borderwidth + 'px'); + bg.enter().append('rect').attr({ + 'class': 'bg', + 'shape-rendering': 'crispEdges' + }); + + bg.call(Color.stroke, opts.bordercolor); + bg.call(Color.fill, opts.bgcolor); + bg.style('stroke-width', opts.borderwidth + 'px'); var scrollBox = legend.selectAll('g.scrollbox') .data([0]); diff --git a/test/jasmine/tests/legend_test.js b/test/jasmine/tests/legend_test.js index f6bf87a045b..1e6b3490aa8 100644 --- a/test/jasmine/tests/legend_test.js +++ b/test/jasmine/tests/legend_test.js @@ -1,3 +1,4 @@ +var Plotly = require('@lib/index'); var Plots = require('@src/plots/plots'); var Lib = require('@src/lib'); @@ -6,113 +7,146 @@ var getLegendData = require('@src/components/legend/get_legend_data'); var helpers = require('@src/components/legend/helpers'); var anchorUtils = require('@src/components/legend/anchor_utils'); +var d3 = require('d3'); +var createGraphDiv = require('../assets/create_graph_div'); +var destroyGraphDiv = require('../assets/destroy_graph_div'); -describe('Test legend:', function() { + +describe('legend defaults', function() { 'use strict'; - describe('supplyLayoutDefaults', function() { - var supplyLayoutDefaults = Legend.supplyLayoutDefaults; + var supplyLayoutDefaults = Legend.supplyLayoutDefaults; - var layoutIn, layoutOut, fullData; + var layoutIn, layoutOut, fullData; - beforeEach(function() { - layoutIn = { - showlegend: true - }; - layoutOut = { - font: Plots.layoutAttributes.font, - bg_color: Plots.layoutAttributes.bg_color - }; - }); + beforeEach(function() { + layoutIn = { + showlegend: true + }; + layoutOut = { + font: Plots.layoutAttributes.font, + bg_color: Plots.layoutAttributes.bg_color + }; + }); - it('should default traceorder to reversed for stack bar charts', function() { - fullData = [ - { type: 'bar' }, - { type: 'bar' }, - { type: 'scatter' } - ]; + it('should default traceorder to reversed for stack bar charts', function() { + fullData = [ + { type: 'bar' }, + { type: 'bar' }, + { type: 'scatter' } + ]; - supplyLayoutDefaults(layoutIn, layoutOut, fullData); - expect(layoutOut.legend.traceorder).toEqual('normal'); + supplyLayoutDefaults(layoutIn, layoutOut, fullData); + expect(layoutOut.legend.traceorder).toEqual('normal'); - layoutOut.barmode = 'stack'; + layoutOut.barmode = 'stack'; - supplyLayoutDefaults(layoutIn, layoutOut, fullData); - expect(layoutOut.legend.traceorder).toEqual('reversed'); - }); + supplyLayoutDefaults(layoutIn, layoutOut, fullData); + expect(layoutOut.legend.traceorder).toEqual('reversed'); + }); - it('should default traceorder to reversed for filled tonext scatter charts', function() { - fullData = [ - { type: 'scatter' }, - { type: 'scatter', fill: 'tonexty' } - ]; + it('should default traceorder to reversed for filled tonext scatter charts', function() { + fullData = [ + { type: 'scatter' }, + { type: 'scatter', fill: 'tonexty' } + ]; - supplyLayoutDefaults(layoutIn, layoutOut, fullData); - expect(layoutOut.legend.traceorder).toEqual('reversed'); - }); + supplyLayoutDefaults(layoutIn, layoutOut, fullData); + expect(layoutOut.legend.traceorder).toEqual('reversed'); + }); - it('should default traceorder to grouped when a group is present', function() { - fullData = [ - { type: 'scatter', legendgroup: 'group' }, - { type: 'scatter'} - ]; + it('should default traceorder to grouped when a group is present', function() { + fullData = [ + { type: 'scatter', legendgroup: 'group' }, + { type: 'scatter'} + ]; - supplyLayoutDefaults(layoutIn, layoutOut, fullData); - expect(layoutOut.legend.traceorder).toEqual('grouped'); + supplyLayoutDefaults(layoutIn, layoutOut, fullData); + expect(layoutOut.legend.traceorder).toEqual('grouped'); - fullData[1].fill = 'tonextx'; + fullData[1].fill = 'tonextx'; - supplyLayoutDefaults(layoutIn, layoutOut, fullData); - expect(layoutOut.legend.traceorder).toEqual('grouped+reversed'); - }); + supplyLayoutDefaults(layoutIn, layoutOut, fullData); + expect(layoutOut.legend.traceorder).toEqual('grouped+reversed'); + }); - it('should default orientation to vertical', function() { - supplyLayoutDefaults(layoutIn, layoutOut, []); - expect(layoutOut.legend.orientation).toEqual('v'); - }); + it('should default orientation to vertical', function() { + supplyLayoutDefaults(layoutIn, layoutOut, []); + expect(layoutOut.legend.orientation).toEqual('v'); + }); + + describe('for horizontal legends', function() { + var layoutInForHorizontalLegends; - describe('for horizontal legends', function() { - var layoutInForHorizontalLegends; - - beforeEach(function() { - layoutInForHorizontalLegends = Lib.extendDeep({ - legend: { - orientation: 'h' - }, - xaxis: { - rangeslider: { - visible: false - } + beforeEach(function() { + layoutInForHorizontalLegends = Lib.extendDeep({ + legend: { + orientation: 'h' + }, + xaxis: { + rangeslider: { + visible: false } - }, layoutIn); - }); + } + }, layoutIn); + }); - it('should default position to bottom left', function() { - supplyLayoutDefaults(layoutInForHorizontalLegends, layoutOut, []); - expect(layoutOut.legend.x).toEqual(0); - expect(layoutOut.legend.xanchor).toEqual('left'); - expect(layoutOut.legend.y).toEqual(-0.1); - expect(layoutOut.legend.yanchor).toEqual('top'); - }); + it('should default position to bottom left', function() { + supplyLayoutDefaults(layoutInForHorizontalLegends, layoutOut, []); + expect(layoutOut.legend.x).toEqual(0); + expect(layoutOut.legend.xanchor).toEqual('left'); + expect(layoutOut.legend.y).toEqual(-0.1); + expect(layoutOut.legend.yanchor).toEqual('top'); + }); - it('should default position to top left if a range slider present', function() { - var mockLayoutIn = Lib.extendDeep({}, layoutInForHorizontalLegends); - mockLayoutIn.xaxis.rangeslider.visible = true; + it('should default position to top left if a range slider present', function() { + var mockLayoutIn = Lib.extendDeep({}, layoutInForHorizontalLegends); + mockLayoutIn.xaxis.rangeslider.visible = true; - supplyLayoutDefaults(mockLayoutIn, layoutOut, []); - expect(layoutOut.legend.x).toEqual(0); - expect(layoutOut.legend.xanchor).toEqual('left'); - expect(layoutOut.legend.y).toEqual(1.1); - expect(layoutOut.legend.yanchor).toEqual('bottom'); - }); + supplyLayoutDefaults(mockLayoutIn, layoutOut, []); + expect(layoutOut.legend.x).toEqual(0); + expect(layoutOut.legend.xanchor).toEqual('left'); + expect(layoutOut.legend.y).toEqual(1.1); + expect(layoutOut.legend.yanchor).toEqual('bottom'); }); }); +}); + +describe('legend getLegendData', function() { + 'use strict'; - describe('getLegendData', function() { - var calcdata, opts, legendData, expected; + var calcdata, opts, legendData, expected; - it('should group legendgroup traces', function() { - calcdata = [ + it('should group legendgroup traces', function() { + calcdata = [ + [{trace: { + type: 'scatter', + visible: true, + legendgroup: 'group', + showlegend: true + + }}], + [{trace: { + type: 'bar', + visible: 'legendonly', + legendgroup: '', + showlegend: true + }}], + [{trace: { + type: 'scatter', + visible: true, + legendgroup: 'group', + showlegend: true + }}] + ]; + opts = { + traceorder: 'grouped' + }; + + legendData = getLegendData(calcdata, opts); + + expected = [ + [ [{trace: { type: 'scatter', visible: true, @@ -120,57 +154,57 @@ describe('Test legend:', function() { showlegend: true }}], - [{trace: { - type: 'bar', - visible: 'legendonly', - legendgroup: '', - showlegend: true - }}], [{trace: { type: 'scatter', visible: true, legendgroup: 'group', showlegend: true }}] - ]; - opts = { - traceorder: 'grouped' - }; - - legendData = getLegendData(calcdata, opts); - - expected = [ - [ - [{trace: { - type: 'scatter', - visible: true, - legendgroup: 'group', - showlegend: true - - }}], - [{trace: { - type: 'scatter', - visible: true, - legendgroup: 'group', - showlegend: true - }}] - ], - [ - [{trace: { - type: 'bar', - visible: 'legendonly', - legendgroup: '', - showlegend: true - }}] - ] - ]; - - expect(legendData).toEqual(expected); - expect(opts._lgroupsLength).toEqual(2); - }); + ], + [ + [{trace: { + type: 'bar', + visible: 'legendonly', + legendgroup: '', + showlegend: true + }}] + ] + ]; + + expect(legendData).toEqual(expected); + expect(opts._lgroupsLength).toEqual(2); + }); + + it('should collapse when data has only one group', function() { + calcdata = [ + [{trace: { + type: 'scatter', + visible: true, + legendgroup: '', + showlegend: true + + }}], + [{trace: { + type: 'bar', + visible: 'legendonly', + legendgroup: '', + showlegend: true + }}], + [{trace: { + type: 'scatter', + visible: true, + legendgroup: '', + showlegend: true + }}] + ]; + opts = { + traceorder: 'grouped' + }; - it('should collapse when data has only one group', function() { - calcdata = [ + legendData = getLegendData(calcdata, opts); + + expected = [ + [ [{trace: { type: 'scatter', visible: true, @@ -190,74 +224,74 @@ describe('Test legend:', function() { legendgroup: '', showlegend: true }}] - ]; - opts = { - traceorder: 'grouped' - }; - - legendData = getLegendData(calcdata, opts); - - expected = [ - [ - [{trace: { - type: 'scatter', - visible: true, - legendgroup: '', - showlegend: true - - }}], - [{trace: { - type: 'bar', - visible: 'legendonly', - legendgroup: '', - showlegend: true - }}], - [{trace: { - type: 'scatter', - visible: true, - legendgroup: '', - showlegend: true - }}] - ] - ]; - - expect(legendData).toEqual(expected); - expect(opts._lgroupsLength).toEqual(1); - }); + ] + ]; - it('should return empty array when legend data has no traces', function() { - calcdata = [ - [{trace: { - type: 'histogram', - visible: true, - legendgroup: '', - showlegend: false + expect(legendData).toEqual(expected); + expect(opts._lgroupsLength).toEqual(1); + }); - }}], - [{trace: { - type: 'box', - visible: 'legendonly', - legendgroup: '', - showlegend: false - }}], - [{trace: { - type: 'heatmap', - visible: true, - legendgroup: '' - }}] - ]; - opts = { - traceorder: 'normal' - }; + it('should return empty array when legend data has no traces', function() { + calcdata = [ + [{trace: { + type: 'histogram', + visible: true, + legendgroup: '', + showlegend: false + + }}], + [{trace: { + type: 'box', + visible: 'legendonly', + legendgroup: '', + showlegend: false + }}], + [{trace: { + type: 'heatmap', + visible: true, + legendgroup: '' + }}] + ]; + opts = { + traceorder: 'normal' + }; + + legendData = getLegendData(calcdata, opts); + expect(legendData).toEqual([]); + }); - legendData = getLegendData(calcdata, opts); - expect(legendData).toEqual([]); - }); + it('should reverse the order when legend.traceorder is set', function() { + calcdata = [ + [{trace: { + type: 'scatter', + visible: true, + legendgroup: '', + showlegend: true - it('should reverse the order when legend.traceorder is set', function() { - calcdata = [ + }}], + [{trace: { + type: 'bar', + visible: 'legendonly', + legendgroup: '', + showlegend: true + }}], + [{trace: { + type: 'box', + visible: true, + legendgroup: '', + showlegend: true + }}] + ]; + opts = { + traceorder: 'reversed' + }; + + legendData = getLegendData(calcdata, opts); + + expected = [ + [ [{trace: { - type: 'scatter', + type: 'box', visible: true, legendgroup: '', showlegend: true @@ -270,106 +304,81 @@ describe('Test legend:', function() { showlegend: true }}], [{trace: { - type: 'box', + type: 'scatter', visible: true, legendgroup: '', showlegend: true }}] - ]; - opts = { - traceorder: 'reversed' - }; - - legendData = getLegendData(calcdata, opts); - - expected = [ - [ - [{trace: { - type: 'box', - visible: true, - legendgroup: '', - showlegend: true - - }}], - [{trace: { - type: 'bar', - visible: 'legendonly', - legendgroup: '', - showlegend: true - }}], - [{trace: { - type: 'scatter', - visible: true, - legendgroup: '', - showlegend: true - }}] - ] - ]; - - expect(legendData).toEqual(expected); - expect(opts._lgroupsLength).toEqual(1); - }); + ] + ]; + + expect(legendData).toEqual(expected); + expect(opts._lgroupsLength).toEqual(1); + }); + + it('should reverse the trace order within groups when reversed+grouped', function() { + calcdata = [ + [{trace: { + type: 'scatter', + visible: true, + legendgroup: 'group', + showlegend: true + + }}], + [{trace: { + type: 'bar', + visible: 'legendonly', + legendgroup: '', + showlegend: true + }}], + [{trace: { + type: 'box', + visible: true, + legendgroup: 'group', + showlegend: true + }}] + ]; + opts = { + traceorder: 'reversed+grouped' + }; - it('should reverse the trace order within groups when reversed+grouped', function() { - calcdata = [ + legendData = getLegendData(calcdata, opts); + + expected = [ + [ [{trace: { - type: 'scatter', + type: 'box', visible: true, legendgroup: 'group', showlegend: true }}], + [{trace: { + type: 'scatter', + visible: true, + legendgroup: 'group', + showlegend: true + }}] + ], + [ [{trace: { type: 'bar', visible: 'legendonly', legendgroup: '', showlegend: true - }}], - [{trace: { - type: 'box', - visible: true, - legendgroup: 'group', - showlegend: true }}] - ]; - opts = { - traceorder: 'reversed+grouped' - }; - - legendData = getLegendData(calcdata, opts); - - expected = [ - [ - [{trace: { - type: 'box', - visible: true, - legendgroup: 'group', - showlegend: true - - }}], - [{trace: { - type: 'scatter', - visible: true, - legendgroup: 'group', - showlegend: true - }}] - ], - [ - [{trace: { - type: 'bar', - visible: 'legendonly', - legendgroup: '', - showlegend: true - }}] - ] - ]; - - expect(legendData).toEqual(expected); - expect(opts._lgroupsLength).toEqual(2); - }); + ] + ]; + + expect(legendData).toEqual(expected); + expect(opts._lgroupsLength).toEqual(2); }); +}); - describe('legendGetsTraces helper', function() { +describe('legend helpers:', function() { + 'use strict'; + + describe('legendGetsTraces', function() { var legendGetsTrace = helpers.legendGetsTrace; it('should return true when trace is visible and supports legend', function() { @@ -380,7 +389,7 @@ describe('Test legend:', function() { }); }); - describe('isGrouped helper', function() { + describe('isGrouped', function() { var isGrouped = helpers.isGrouped; it('should return true when trace is visible and supports legend', function() { @@ -392,7 +401,7 @@ describe('Test legend:', function() { }); }); - describe('isReversed helper', function() { + describe('isReversed', function() { var isReversed = helpers.isReversed; it('should return true when trace is visible and supports legend', function() { @@ -403,8 +412,12 @@ describe('Test legend:', function() { expect(isReversed({ traceorder: 'reversed' })).toBe(true); }); }); +}); - describe('isRightAnchor anchor util', function() { +describe('legend anchor utils:', function() { + 'use strict'; + + describe('isRightAnchor', function() { var isRightAnchor = anchorUtils.isRightAnchor; var threshold = 2 / 3; @@ -425,7 +438,7 @@ describe('Test legend:', function() { }); }); - describe('isCenterAnchor anchor util', function() { + describe('isCenterAnchor', function() { var isCenterAnchor = anchorUtils.isCenterAnchor; var threshold0 = 1 / 3; var threshold1 = 2 / 3; @@ -447,7 +460,7 @@ describe('Test legend:', function() { }); }); - describe('isBottomAnchor anchor util', function() { + describe('isBottomAnchor', function() { var isBottomAnchor = anchorUtils.isBottomAnchor; var threshold = 1 / 3; @@ -468,7 +481,7 @@ describe('Test legend:', function() { }); }); - describe('isMiddleAnchor anchor util', function() { + describe('isMiddleAnchor', function() { var isMiddleAnchor = anchorUtils.isMiddleAnchor; var threshold0 = 1 / 3; var threshold1 = 2 / 3; @@ -489,5 +502,41 @@ describe('Test legend:', function() { }); }); }); +}); + +describe('legend relayout update', function() { + 'use strict'; + + afterEach(destroyGraphDiv); + + it('should update border styling', function(done) { + var mock = require('@mocks/0.json'), + mockCopy = Lib.extendDeep({}, mock), + gd = createGraphDiv(); + + function assertLegendStyle(bgColor, borderColor, borderWidth) { + var node = d3.select('g.legend').select('rect'); + expect(node.style('fill')).toEqual(bgColor); + expect(node.style('stroke')).toEqual(borderColor); + expect(node.style('stroke-width')).toEqual(borderWidth + 'px'); + } + + Plotly.plot(gd, mockCopy.data, mockCopy.layout).then(function() { + assertLegendStyle('rgb(255, 255, 255)', 'rgb(0, 0, 0)', 1); + + return Plotly.relayout(gd, { + 'legend.bordercolor': 'red', + 'legend.bgcolor': 'blue' + }); + }).then(function() { + assertLegendStyle('rgb(0, 0, 255)', 'rgb(255, 0, 0)', 1); + + return Plotly.relayout(gd, 'legend.borderwidth', 10); + }).then(function() { + assertLegendStyle('rgb(0, 0, 255)', 'rgb(255, 0, 0)', 10); + + done(); + }); + }); });