@@ -83,6 +83,21 @@ describe('legend defaults', function () {
8383 expect ( layoutOut . showlegend ) . toBe ( false ) ;
8484 } ) ;
8585
86+ it ( 'defaults itemheight to 6 and clamps values below the minimum' , function ( ) {
87+ fullData = allShown ( [ { type : 'scatter' } , { type : 'scatter' } ] ) ;
88+
89+ supplyLayoutDefaults ( { } , layoutOut , fullData ) ;
90+ expect ( layoutOut . legend . itemheight ) . toBe ( 6 ) ;
91+
92+ layoutOut = { font : Plots . layoutAttributes . font , bg_color : Plots . layoutAttributes . bg_color } ;
93+ supplyLayoutDefaults ( { showlegend : true , legend : { itemheight : 1 } } , layoutOut , fullData ) ;
94+ expect ( layoutOut . legend . itemheight ) . toBe ( 6 ) ;
95+
96+ layoutOut = { font : Plots . layoutAttributes . font , bg_color : Plots . layoutAttributes . bg_color } ;
97+ supplyLayoutDefaults ( { showlegend : true , legend : { itemheight : 24 } } , layoutOut , fullData ) ;
98+ expect ( layoutOut . legend . itemheight ) . toBe ( 24 ) ;
99+ } ) ;
100+
86101 it ( 'shows with one visible pie' , function ( ) {
87102 fullData = allShown ( [ { type : 'pie' } ] ) ;
88103
@@ -3498,4 +3513,135 @@ describe('legend title click', function() {
34983513 }
34993514 } ) . then ( done , done . fail ) ;
35003515 } ) ;
3501- } ) ;
3516+ } ) ;
3517+ describe ( 'legend itemheight:' , function ( ) {
3518+ 'use strict' ;
3519+
3520+ var gd ;
3521+
3522+ beforeEach ( function ( ) {
3523+ gd = createGraphDiv ( ) ;
3524+ } ) ;
3525+
3526+ afterEach ( destroyGraphDiv ) ;
3527+
3528+ function fillPathD ( ) {
3529+ return d3Select ( gd ) . select ( 'g.legendfill' ) . select ( 'path' ) . attr ( 'd' ) ;
3530+ }
3531+
3532+ function linePathD ( ) {
3533+ return d3Select ( gd ) . select ( 'g.legendlines' ) . select ( 'path' ) . attr ( 'd' ) ;
3534+ }
3535+
3536+ // The toggle rect is sized to the computed row height, see setRect in draw.js
3537+ function rowHeights ( ) {
3538+ var heights = [ ] ;
3539+ d3Select ( gd ) . selectAll ( 'rect.legendtoggle' ) . each ( function ( ) {
3540+ heights . push ( + this . getAttribute ( 'height' ) ) ;
3541+ } ) ;
3542+ return heights ;
3543+ }
3544+
3545+ var filled = [
3546+ { x : [ 1 , 2 ] , y : [ 1 , 2 ] , fill : 'tozeroy' , name : 'a' } ,
3547+ { x : [ 1 , 2 ] , y : [ 2 , 3 ] , fill : 'tozeroy' , name : 'b' }
3548+ ] ;
3549+
3550+ it ( 'reproduces the historical 6px swatch at the default' , function ( done ) {
3551+ Plotly . newPlot ( gd , filled , { showlegend : true } )
3552+ . then ( function ( ) {
3553+ expect ( gd . _fullLayout . legend . itemheight ) . toBe ( 6 ) ;
3554+ expect ( fillPathD ( ) ) . toBe ( 'M5,0h30v6h-30z' ) ;
3555+ } )
3556+ . then ( done , done . fail ) ;
3557+ } ) ;
3558+
3559+ it ( 'grows the fill swatch to the requested height' , function ( done ) {
3560+ Plotly . newPlot ( gd , filled , { showlegend : true , legend : { itemheight : 24 } } )
3561+ . then ( function ( ) {
3562+ expect ( fillPathD ( ) ) . toBe ( 'M5,0h30v24h-30z' ) ;
3563+ } )
3564+ . then ( done , done . fail ) ;
3565+ } ) ;
3566+
3567+ it ( 'grows the swatch downwards, leaving the line on top of the fill' , function ( done ) {
3568+ var dfltLine ;
3569+
3570+ Plotly . newPlot ( gd , filled , { showlegend : true } )
3571+ . then ( function ( ) {
3572+ dfltLine = linePathD ( ) ;
3573+ return Plotly . relayout ( gd , 'legend.itemheight' , 30 ) ;
3574+ } )
3575+ . then ( function ( ) {
3576+ // the line marks the top edge of the fill, exactly as in the plot itself
3577+ expect ( linePathD ( ) ) . toBe ( dfltLine ) ;
3578+ expect ( fillPathD ( ) ) . toBe ( 'M5,0h30v30h-30z' ) ;
3579+ } )
3580+ . then ( done , done . fail ) ;
3581+ } ) ;
3582+
3583+ it ( 'does not move the swatch of a trace without fill' , function ( done ) {
3584+ var unfilled = [
3585+ { x : [ 1 , 2 ] , y : [ 1 , 2 ] , name : 'a' } ,
3586+ { x : [ 1 , 2 ] , y : [ 2 , 3 ] , name : 'b' }
3587+ ] ;
3588+ var dfltLine ;
3589+
3590+ Plotly . newPlot ( gd , unfilled , { showlegend : true } )
3591+ . then ( function ( ) {
3592+ dfltLine = linePathD ( ) ;
3593+ return Plotly . relayout ( gd , 'legend.itemheight' , 40 ) ;
3594+ } )
3595+ . then ( function ( ) {
3596+ expect ( linePathD ( ) ) . toBe ( dfltLine ) ;
3597+ } )
3598+ . then ( done , done . fail ) ;
3599+ } ) ;
3600+
3601+ it ( 'grows each legend row so taller swatches do not overlap' , function ( done ) {
3602+ var dflt ;
3603+
3604+ Plotly . newPlot ( gd , filled , { showlegend : true } )
3605+ . then ( function ( ) {
3606+ dflt = rowHeights ( ) ;
3607+ expect ( dflt . length ) . toBe ( 2 ) ;
3608+ return Plotly . relayout ( gd , 'legend.itemheight' , 40 ) ;
3609+ } )
3610+ . then ( function ( ) {
3611+ var grown = rowHeights ( ) ;
3612+ expect ( grown . length ) . toBe ( dflt . length ) ;
3613+ grown . forEach ( function ( h , i ) {
3614+ expect ( h ) . toBeGreaterThan ( dflt [ i ] ) ;
3615+ // Math.max(textHeight, 16, itemheight + 10) + 3
3616+ expect ( h ) . toBe ( 53 ) ;
3617+ } ) ;
3618+ } )
3619+ . then ( done , done . fail ) ;
3620+ } ) ;
3621+
3622+ it ( 'leaves row heights untouched at the default' , function ( done ) {
3623+ var dflt ;
3624+
3625+ Plotly . newPlot ( gd , filled , { showlegend : true } )
3626+ . then ( function ( ) {
3627+ dflt = rowHeights ( ) ;
3628+ return Plotly . relayout ( gd , 'legend.itemheight' , 6 ) ;
3629+ } )
3630+ . then ( function ( ) {
3631+ expect ( rowHeights ( ) ) . toEqual ( dflt ) ;
3632+ } )
3633+ . then ( done , done . fail ) ;
3634+ } ) ;
3635+
3636+ it ( 'applies to unified hover labels without error' , function ( done ) {
3637+ Plotly . newPlot ( gd , filled , {
3638+ showlegend : true ,
3639+ hovermode : 'x unified' ,
3640+ legend : { itemheight : 18 }
3641+ } )
3642+ . then ( function ( ) {
3643+ expect ( gd . _fullLayout . legend . itemheight ) . toBe ( 18 ) ;
3644+ } )
3645+ . then ( done , done . fail ) ;
3646+ } ) ;
3647+ } ) ;
0 commit comments