From c3a64586e02a582e7ec3c354a118c8f4ba6ab974 Mon Sep 17 00:00:00 2001 From: archmoj Date: Wed, 24 Nov 2021 12:49:29 -0500 Subject: [PATCH 1/7] implement grouptitlesfont for hover and legend --- src/components/fx/hover.js | 3 +- src/components/fx/layout_attributes.js | 18 ++++++++--- src/components/fx/layout_defaults.js | 2 ++ src/plots/layout_attributes.js | 8 +++++ src/plots/plots.js | 20 ++++++------ test/image/baselines/legendrank.png | Bin 17209 -> 16934 bytes test/image/mocks/legendrank.json | 12 +++++++ test/plot-schema.json | 42 +++++++++++++++++++++++++ 8 files changed, 90 insertions(+), 15 deletions(-) diff --git a/src/components/fx/hover.js b/src/components/fx/hover.js index 76b7bbd1f8a..79c0927782b 100644 --- a/src/components/fx/hover.js +++ b/src/components/fx/hover.js @@ -1144,7 +1144,8 @@ function createHoverText(hoverData, opts) { // Draw unified hover label mockLegend._inHover = true; - mockLegend._groupTitleFont = font; + mockLegend._groupTitleFont = hoverlabel.grouptitlesfont; + legendDraw(gd, mockLegend); // Position the hover diff --git a/src/components/fx/layout_attributes.js b/src/components/fx/layout_attributes.js index e2deaa1ed3f..f9b742a6bf4 100644 --- a/src/components/fx/layout_attributes.js +++ b/src/components/fx/layout_attributes.js @@ -2,12 +2,14 @@ var constants = require('./constants'); -var fontAttrs = require('../../plots/font_attributes')({ +var fontAttrs = require('../../plots/font_attributes'); + +var font = fontAttrs({ editType: 'none', description: 'Sets the default hover label font used by all traces on the graph.' }); -fontAttrs.family.dflt = constants.HOVERFONT; -fontAttrs.size.dflt = constants.HOVERFONTSIZE; +font.family.dflt = constants.HOVERFONT; +font.size.dflt = constants.HOVERFONTSIZE; module.exports = { clickmode: { @@ -118,7 +120,7 @@ module.exports = { 'Sets the border color of all hover labels on graph.' ].join(' ') }, - font: fontAttrs, + font: font, align: { valType: 'enumerated', values: ['left', 'right', 'auto'], @@ -143,6 +145,14 @@ module.exports = { '`namelength - 3` characters and add an ellipsis.' ].join(' ') }, + grouptitlesfont: fontAttrs({ + editType: 'none', + description: [ + 'Sets the font for group titles in hover (unified modes).', + 'Defaults to `hoverlabel.font`.' + ].join(' ') + }), + editType: 'none' }, selectdirection: { diff --git a/src/components/fx/layout_defaults.js b/src/components/fx/layout_defaults.js index 655d708a249..46415747448 100644 --- a/src/components/fx/layout_defaults.js +++ b/src/components/fx/layout_defaults.js @@ -34,4 +34,6 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) { } handleHoverLabelDefaults(layoutIn, layoutOut, coerce); + + Lib.coerceFont(coerce, 'hoverlabel.grouptitlesfont', layoutOut.hoverlabel.font); }; diff --git a/src/plots/layout_attributes.js b/src/plots/layout_attributes.js index 100503d9c21..3f30d3e2707 100644 --- a/src/plots/layout_attributes.js +++ b/src/plots/layout_attributes.js @@ -309,6 +309,14 @@ module.exports = { 'c) One trace is explicitly given with `showlegend: true`.' ].join(' ') }, + legendgrouptitlesfont: fontAttrs({ + editType: 'legend', + description: [ + 'Sets the font for group titles in legend.', + 'Defaults to `legend.font` with its size increased about 10%.' + ].join(' ') + }), + colorway: { valType: 'colorlist', dflt: colorAttrs.defaults, diff --git a/src/plots/plots.js b/src/plots/plots.js index d4ec300d719..2b118487caf 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -1323,9 +1323,7 @@ plots.supplyTraceDefaults = function(traceIn, traceOut, colorIndex, layout, trac coerce('legendgroup'); var titleText = coerce('legendgrouptitle.text'); if(titleText) { - Lib.coerceFont(coerce, 'legendgrouptitle.font', Lib.extendFlat({}, layout.font, { - size: Math.round(layout.font.size * 1.1) // default to larger font size - })); + Lib.coerceFont(coerce, 'legendgrouptitle.font', layout.legendgrouptitlesfont); } coerce('legendrank'); @@ -1475,16 +1473,18 @@ plots.supplyLayoutGlobalDefaults = function(layoutIn, layoutOut, formatObj) { coerce('autotypenumbers'); - var globalFont = Lib.coerceFont(coerce, 'font'); + var font = Lib.coerceFont(coerce, 'font'); + var fontSize = font.size; - coerce('title.text', layoutOut._dfltTitle.plot); + Lib.coerceFont(coerce, 'legendgrouptitlesfont', Lib.extendFlat({}, font, { + size: Math.round(fontSize * 1.1) + })); - Lib.coerceFont(coerce, 'title.font', { - family: globalFont.family, - size: Math.round(globalFont.size * 1.4), - color: globalFont.color - }); + Lib.coerceFont(coerce, 'title.font', Lib.extendFlat({}, font, { + size: Math.round(fontSize * 1.4) + })); + coerce('title.text', layoutOut._dfltTitle.plot); coerce('title.xref'); coerce('title.yref'); coerce('title.x'); diff --git a/test/image/baselines/legendrank.png b/test/image/baselines/legendrank.png index a25a14c5d877228a4eac692f1e317235d3f15413..08971ed4ccb244a407cc4ea42e8769e94ef060a8 100644 GIT binary patch literal 16934 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 C=QlwB literal 17209 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 will show the whole name if it is less than that many characters, but if it is longer, will truncate to `namelength - 3` characters and add an ellipsis.", "dflt": 15, @@ -2857,6 +2878,27 @@ ] } }, + "legendgrouptitlesfont": { + "color": { + "editType": "legend", + "valType": "color" + }, + "description": "Sets the font for group titles in legend. Defaults to `legend.font` with its size increased about 10%.", + "editType": "legend", + "family": { + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*.", + "editType": "legend", + "noBlank": true, + "strict": true, + "valType": "string" + }, + "role": "object", + "size": { + "editType": "legend", + "min": 1, + "valType": "number" + } + }, "mapbox": { "_arrayAttrRegexps": [ {} From bf2e51a379c1be9e2cc2ab8ccac1ec0533ecb699 Mon Sep 17 00:00:00 2001 From: archmoj Date: Wed, 24 Nov 2021 13:28:30 -0500 Subject: [PATCH 2/7] fx --- src/components/fx/layout_attributes.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/fx/layout_attributes.js b/src/components/fx/layout_attributes.js index f9b742a6bf4..c3a99d48dae 100644 --- a/src/components/fx/layout_attributes.js +++ b/src/components/fx/layout_attributes.js @@ -121,6 +121,13 @@ module.exports = { ].join(' ') }, font: font, + grouptitlesfont: fontAttrs({ + editType: 'none', + description: [ + 'Sets the font for group titles in hover (unified modes).', + 'Defaults to `hoverlabel.font`.' + ].join(' ') + }), align: { valType: 'enumerated', values: ['left', 'right', 'auto'], @@ -145,13 +152,6 @@ module.exports = { '`namelength - 3` characters and add an ellipsis.' ].join(' ') }, - grouptitlesfont: fontAttrs({ - editType: 'none', - description: [ - 'Sets the font for group titles in hover (unified modes).', - 'Defaults to `hoverlabel.font`.' - ].join(' ') - }), editType: 'none' }, From 339d37bb8154f27b60a136730f5287b91da4d05c Mon Sep 17 00:00:00 2001 From: archmoj Date: Wed, 24 Nov 2021 13:38:47 -0500 Subject: [PATCH 3/7] log for PR 6040 --- draftlogs/6040_add.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 draftlogs/6040_add.md diff --git a/draftlogs/6040_add.md b/draftlogs/6040_add.md new file mode 100644 index 00000000000..e4b336a44b1 --- /dev/null +++ b/draftlogs/6040_add.md @@ -0,0 +1 @@ + - Implement legendgrouptitlesfont and hoverlabel.grouptitlesfont [[#6040](https://github.com/plotly/plotly.js/pull/6040)] From ebb9c63d30d808b776b97d1c6f7099d5f01ad9f0 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Fri, 3 Dec 2021 14:24:18 -0500 Subject: [PATCH 4/7] grouptitlesfont > grouptitlefont --- draftlogs/6040_add.md | 2 +- src/components/fx/hover.js | 2 +- src/components/fx/layout_attributes.js | 2 +- src/components/fx/layout_defaults.js | 2 +- src/plots/layout_attributes.js | 2 +- src/plots/plots.js | 4 ++-- test/image/mocks/legendrank.json | 4 ++-- test/plot-schema.json | 4 ++-- 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/draftlogs/6040_add.md b/draftlogs/6040_add.md index e4b336a44b1..5f860cf1ebb 100644 --- a/draftlogs/6040_add.md +++ b/draftlogs/6040_add.md @@ -1 +1 @@ - - Implement legendgrouptitlesfont and hoverlabel.grouptitlesfont [[#6040](https://github.com/plotly/plotly.js/pull/6040)] + - Implement legendgrouptitlefont and hoverlabel.grouptitlefont [[#6040](https://github.com/plotly/plotly.js/pull/6040)] diff --git a/src/components/fx/hover.js b/src/components/fx/hover.js index 79c0927782b..e04adb0feeb 100644 --- a/src/components/fx/hover.js +++ b/src/components/fx/hover.js @@ -1144,7 +1144,7 @@ function createHoverText(hoverData, opts) { // Draw unified hover label mockLegend._inHover = true; - mockLegend._groupTitleFont = hoverlabel.grouptitlesfont; + mockLegend._groupTitleFont = hoverlabel.grouptitlefont; legendDraw(gd, mockLegend); diff --git a/src/components/fx/layout_attributes.js b/src/components/fx/layout_attributes.js index c3a99d48dae..af5ce341e21 100644 --- a/src/components/fx/layout_attributes.js +++ b/src/components/fx/layout_attributes.js @@ -121,7 +121,7 @@ module.exports = { ].join(' ') }, font: font, - grouptitlesfont: fontAttrs({ + grouptitlefont: fontAttrs({ editType: 'none', description: [ 'Sets the font for group titles in hover (unified modes).', diff --git a/src/components/fx/layout_defaults.js b/src/components/fx/layout_defaults.js index 46415747448..304e6caf11f 100644 --- a/src/components/fx/layout_defaults.js +++ b/src/components/fx/layout_defaults.js @@ -35,5 +35,5 @@ module.exports = function supplyLayoutDefaults(layoutIn, layoutOut) { handleHoverLabelDefaults(layoutIn, layoutOut, coerce); - Lib.coerceFont(coerce, 'hoverlabel.grouptitlesfont', layoutOut.hoverlabel.font); + Lib.coerceFont(coerce, 'hoverlabel.grouptitlefont', layoutOut.hoverlabel.font); }; diff --git a/src/plots/layout_attributes.js b/src/plots/layout_attributes.js index 3f30d3e2707..06d66dfc20d 100644 --- a/src/plots/layout_attributes.js +++ b/src/plots/layout_attributes.js @@ -309,7 +309,7 @@ module.exports = { 'c) One trace is explicitly given with `showlegend: true`.' ].join(' ') }, - legendgrouptitlesfont: fontAttrs({ + legendgrouptitlefont: fontAttrs({ editType: 'legend', description: [ 'Sets the font for group titles in legend.', diff --git a/src/plots/plots.js b/src/plots/plots.js index 2b118487caf..79fcdc56c10 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -1323,7 +1323,7 @@ plots.supplyTraceDefaults = function(traceIn, traceOut, colorIndex, layout, trac coerce('legendgroup'); var titleText = coerce('legendgrouptitle.text'); if(titleText) { - Lib.coerceFont(coerce, 'legendgrouptitle.font', layout.legendgrouptitlesfont); + Lib.coerceFont(coerce, 'legendgrouptitle.font', layout.legendgrouptitlefont); } coerce('legendrank'); @@ -1476,7 +1476,7 @@ plots.supplyLayoutGlobalDefaults = function(layoutIn, layoutOut, formatObj) { var font = Lib.coerceFont(coerce, 'font'); var fontSize = font.size; - Lib.coerceFont(coerce, 'legendgrouptitlesfont', Lib.extendFlat({}, font, { + Lib.coerceFont(coerce, 'legendgrouptitlefont', Lib.extendFlat({}, font, { size: Math.round(fontSize * 1.1) })); diff --git a/test/image/mocks/legendrank.json b/test/image/mocks/legendrank.json index 6d5a2c76086..d60e2a39af9 100644 --- a/test/image/mocks/legendrank.json +++ b/test/image/mocks/legendrank.json @@ -38,13 +38,13 @@ }, "hovermode": "x unified", "hoverlabel": { - "grouptitlesfont": { + "grouptitlefont": { "family": "Raleway", "color": "red", "size": 16 } }, - "legendgrouptitlesfont": { + "legendgrouptitlefont": { "family": "Times New Roman", "color": "orange", "size": 14 diff --git a/test/plot-schema.json b/test/plot-schema.json index 8860d1dfa77..90b7f67253d 100644 --- a/test/plot-schema.json +++ b/test/plot-schema.json @@ -2494,7 +2494,7 @@ "valType": "number" } }, - "grouptitlesfont": { + "grouptitlefont": { "color": { "editType": "none", "valType": "color" @@ -2878,7 +2878,7 @@ ] } }, - "legendgrouptitlesfont": { + "legendgrouptitlefont": { "color": { "editType": "legend", "valType": "color" From c62e6327dbdc1451e33f450770a4eef54ebc01f7 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Fri, 3 Dec 2021 15:32:15 -0500 Subject: [PATCH 5/7] move grouptitlefont inside legend object --- draftlogs/6040_add.md | 2 +- src/components/fx/hover.js | 4 ++- src/components/fx/hoverlabel_defaults.js | 8 +++-- src/components/legend/attributes.js | 7 ++++ src/components/legend/defaults.js | 29 ++++++++++++---- src/plot_api/plot_api.js | 2 +- src/plots/layout_attributes.js | 7 ---- src/plots/plots.js | 10 +----- test/image/mocks/legendrank.json | 10 +++--- test/plot-schema.json | 42 ++++++++++++------------ 10 files changed, 68 insertions(+), 53 deletions(-) diff --git a/draftlogs/6040_add.md b/draftlogs/6040_add.md index 5f860cf1ebb..452e793c7b8 100644 --- a/draftlogs/6040_add.md +++ b/draftlogs/6040_add.md @@ -1 +1 @@ - - Implement legendgrouptitlefont and hoverlabel.grouptitlefont [[#6040](https://github.com/plotly/plotly.js/pull/6040)] + - Implement legend.grouptitlefont and hoverlabel.grouptitlefont [[#6040](https://github.com/plotly/plotly.js/pull/6040)] diff --git a/src/components/fx/hover.js b/src/components/fx/hover.js index e04adb0feeb..0643c004bcd 100644 --- a/src/components/fx/hover.js +++ b/src/components/fx/hover.js @@ -1103,7 +1103,9 @@ function createHoverText(hoverData, opts) { orientation: 'v' } }; - var mockLayoutOut = {}; + var mockLayoutOut = { + font: font + }; legendSupplyDefaults(mockLayoutIn, mockLayoutOut, gd._fullData); var mockLegend = mockLayoutOut.legend; diff --git a/src/components/fx/hoverlabel_defaults.js b/src/components/fx/hoverlabel_defaults.js index c0e62bbd9d4..c27078a191b 100644 --- a/src/components/fx/hoverlabel_defaults.js +++ b/src/components/fx/hoverlabel_defaults.js @@ -7,9 +7,13 @@ var isUnifiedHover = require('./helpers').isUnifiedHover; module.exports = function handleHoverLabelDefaults(contIn, contOut, coerce, opts) { opts = opts || {}; + var hasLegend = + contOut.legend && + contOut.legend.font; + function inheritFontAttr(attr) { if(!opts.font[attr]) { - opts.font[attr] = contOut.legend ? contOut.legend.font[attr] : contOut.font[attr]; + opts.font[attr] = hasLegend ? contOut.legend.font[attr] : contOut.font[attr]; } } @@ -20,7 +24,7 @@ module.exports = function handleHoverLabelDefaults(contIn, contOut, coerce, opts inheritFontAttr('family'); inheritFontAttr('color'); - if(contOut.legend) { + if(hasLegend) { if(!opts.bgcolor) opts.bgcolor = Color.combine(contOut.legend.bgcolor, contOut.paper_bgcolor); if(!opts.bordercolor) opts.bordercolor = contOut.legend.bordercolor; } else { diff --git a/src/components/legend/attributes.js b/src/components/legend/attributes.js index e5713595d39..f294d2471f0 100644 --- a/src/components/legend/attributes.js +++ b/src/components/legend/attributes.js @@ -30,6 +30,13 @@ module.exports = { editType: 'legend', description: 'Sets the font used to text the legend items.' }), + grouptitlefont: fontAttrs({ + editType: 'legend', + description: [ + 'Sets the font for group titles in legend.', + 'Defaults to `legend.font` with its size increased about 10%.' + ].join(' ') + }), orientation: { valType: 'enumerated', values: ['v', 'h'], diff --git a/src/components/legend/defaults.js b/src/components/legend/defaults.js index 7862a631956..b46b4f1355c 100644 --- a/src/components/legend/defaults.js +++ b/src/components/legend/defaults.js @@ -4,6 +4,7 @@ var Registry = require('../../registry'); var Lib = require('../../lib'); var Template = require('../../plot_api/plot_template'); +var plotsAttrs = require('../../plots/attributes'); var attributes = require('./attributes'); var basePlotLayoutAttributes = require('../../plots/layout_attributes'); var helpers = require('./helpers'); @@ -11,13 +12,31 @@ var helpers = require('./helpers'); module.exports = function legendDefaults(layoutIn, layoutOut, fullData) { var containerIn = layoutIn.legend || {}; + var containerOut = Template.newContainer(layoutOut, 'legend'); + + function coerce(attr, dflt) { + return Lib.coerce(containerIn, containerOut, attributes, attr, dflt); + } + + var trace; + + function traceCoerce(attr, dflt) { + var traceIn = trace._input; + var traceOut = trace; + return Lib.coerce(traceIn, traceOut, plotsAttrs, attr, dflt); + } + + var globalFont = layoutOut.font || {}; + var grouptitlefont = Lib.coerceFont(coerce, 'grouptitlefont', Lib.extendFlat({}, globalFont, { + size: Math.round(globalFont.size * 1.1) + })); var legendTraceCount = 0; var legendReallyHasATrace = false; var defaultOrder = 'normal'; for(var i = 0; i < fullData.length; i++) { - var trace = fullData[i]; + trace = fullData[i]; if(!trace.visible) continue; @@ -44,6 +63,8 @@ module.exports = function legendDefaults(layoutIn, layoutOut, fullData) { legendTraceCount++; } } + + Lib.coerceFont(traceCoerce, 'legendgrouptitle.font', grouptitlefont); } if((Registry.traceIs(trace, 'bar') && layoutOut.barmode === 'stack') || @@ -64,12 +85,6 @@ module.exports = function legendDefaults(layoutIn, layoutOut, fullData) { if(showLegend === false && !containerIn.uirevision) return; - var containerOut = Template.newContainer(layoutOut, 'legend'); - - function coerce(attr, dflt) { - return Lib.coerce(containerIn, containerOut, attributes, attr, dflt); - } - coerce('uirevision', layoutOut.uirevision); if(showLegend === false) return; diff --git a/src/plot_api/plot_api.js b/src/plot_api/plot_api.js index 1a9386a3915..15caf485e5f 100644 --- a/src/plot_api/plot_api.js +++ b/src/plot_api/plot_api.js @@ -1719,7 +1719,7 @@ function cleanDeprecatedAttributeKeys(aobj) { if((key === 'title' || oldAxisTitleRegex.test(key) || colorbarRegex.test(key)) && (typeof value === 'string' || typeof value === 'number')) { replace(key, key.replace('title', 'title.text')); - } else if(key.indexOf('titlefont') > -1) { + } else if(key.indexOf('titlefont') > -1 && key.indexOf('grouptitlefont') === -1) { replace(key, key.replace('titlefont', 'title.font')); } else if(key.indexOf('titleposition') > -1) { replace(key, key.replace('titleposition', 'title.position')); diff --git a/src/plots/layout_attributes.js b/src/plots/layout_attributes.js index 06d66dfc20d..ee955e5bfc7 100644 --- a/src/plots/layout_attributes.js +++ b/src/plots/layout_attributes.js @@ -309,13 +309,6 @@ module.exports = { 'c) One trace is explicitly given with `showlegend: true`.' ].join(' ') }, - legendgrouptitlefont: fontAttrs({ - editType: 'legend', - description: [ - 'Sets the font for group titles in legend.', - 'Defaults to `legend.font` with its size increased about 10%.' - ].join(' ') - }), colorway: { valType: 'colorlist', diff --git a/src/plots/plots.js b/src/plots/plots.js index 79fcdc56c10..cda76cf8f37 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -1321,11 +1321,7 @@ plots.supplyTraceDefaults = function(traceIn, traceOut, colorIndex, layout, trac ); coerce('legendgroup'); - var titleText = coerce('legendgrouptitle.text'); - if(titleText) { - Lib.coerceFont(coerce, 'legendgrouptitle.font', layout.legendgrouptitlefont); - } - + coerce('legendgrouptitle.text'); coerce('legendrank'); traceOut._dfltShowLegend = true; @@ -1476,10 +1472,6 @@ plots.supplyLayoutGlobalDefaults = function(layoutIn, layoutOut, formatObj) { var font = Lib.coerceFont(coerce, 'font'); var fontSize = font.size; - Lib.coerceFont(coerce, 'legendgrouptitlefont', Lib.extendFlat({}, font, { - size: Math.round(fontSize * 1.1) - })); - Lib.coerceFont(coerce, 'title.font', Lib.extendFlat({}, font, { size: Math.round(fontSize * 1.4) })); diff --git a/test/image/mocks/legendrank.json b/test/image/mocks/legendrank.json index d60e2a39af9..608600caa66 100644 --- a/test/image/mocks/legendrank.json +++ b/test/image/mocks/legendrank.json @@ -44,10 +44,12 @@ "size": 16 } }, - "legendgrouptitlefont": { - "family": "Times New Roman", - "color": "orange", - "size": 14 + "legend": { + "grouptitlefont": { + "family": "Times New Roman", + "color": "orange", + "size": 14 + } }, "margin": { "t": 50, diff --git a/test/plot-schema.json b/test/plot-schema.json index 90b7f67253d..d83b6b38ce2 100644 --- a/test/plot-schema.json +++ b/test/plot-schema.json @@ -2714,6 +2714,27 @@ "togglegroup" ] }, + "grouptitlefont": { + "color": { + "editType": "legend", + "valType": "color" + }, + "description": "Sets the font for group titles in legend. Defaults to `legend.font` with its size increased about 10%.", + "editType": "legend", + "family": { + "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*.", + "editType": "legend", + "noBlank": true, + "strict": true, + "valType": "string" + }, + "role": "object", + "size": { + "editType": "legend", + "min": 1, + "valType": "number" + } + }, "itemclick": { "description": "Determines the behavior on legend item click. *toggle* toggles the visibility of the item clicked on the graph. *toggleothers* makes the clicked item the sole visible item on the graph. *false* disables legend item click interactions.", "dflt": "toggle", @@ -2878,27 +2899,6 @@ ] } }, - "legendgrouptitlefont": { - "color": { - "editType": "legend", - "valType": "color" - }, - "description": "Sets the font for group titles in legend. Defaults to `legend.font` with its size increased about 10%.", - "editType": "legend", - "family": { - "description": "HTML font family - the typeface that will be applied by the web browser. The web browser will only be able to apply a font if it is available on the system which it operates. Provide multiple font families, separated by commas, to indicate the preference in which to apply fonts if they aren't available on the system. The Chart Studio Cloud (at https://chart-studio.plotly.com or on-premise) generates images on a server, where only a select number of fonts are installed and supported. These include *Arial*, *Balto*, *Courier New*, *Droid Sans*,, *Droid Serif*, *Droid Sans Mono*, *Gravitas One*, *Old Standard TT*, *Open Sans*, *Overpass*, *PT Sans Narrow*, *Raleway*, *Times New Roman*.", - "editType": "legend", - "noBlank": true, - "strict": true, - "valType": "string" - }, - "role": "object", - "size": { - "editType": "legend", - "min": 1, - "valType": "number" - } - }, "mapbox": { "_arrayAttrRegexps": [ {} From f3095bf04f6f08c1df478c47273ae7ec64a24956 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 6 Dec 2021 11:08:38 -0500 Subject: [PATCH 6/7] add no legend to layout when legend does not show up --- src/components/fx/hoverlabel_defaults.js | 4 +--- src/components/legend/defaults.js | 8 +++++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/fx/hoverlabel_defaults.js b/src/components/fx/hoverlabel_defaults.js index c27078a191b..6e8627e24a7 100644 --- a/src/components/fx/hoverlabel_defaults.js +++ b/src/components/fx/hoverlabel_defaults.js @@ -7,9 +7,7 @@ var isUnifiedHover = require('./helpers').isUnifiedHover; module.exports = function handleHoverLabelDefaults(contIn, contOut, coerce, opts) { opts = opts || {}; - var hasLegend = - contOut.legend && - contOut.legend.font; + var hasLegend = contOut.legend; function inheritFontAttr(attr) { if(!opts.font[attr]) { diff --git a/src/components/legend/defaults.js b/src/components/legend/defaults.js index b46b4f1355c..70afc29ab33 100644 --- a/src/components/legend/defaults.js +++ b/src/components/legend/defaults.js @@ -19,12 +19,11 @@ module.exports = function legendDefaults(layoutIn, layoutOut, fullData) { } var trace; - - function traceCoerce(attr, dflt) { + var traceCoerce = function(attr, dflt) { var traceIn = trace._input; var traceOut = trace; return Lib.coerce(traceIn, traceOut, plotsAttrs, attr, dflt); - } + }; var globalFont = layoutOut.font || {}; var grouptitlefont = Lib.coerceFont(coerce, 'grouptitlefont', Lib.extendFlat({}, globalFont, { @@ -83,6 +82,9 @@ module.exports = function legendDefaults(layoutIn, layoutOut, fullData) { basePlotLayoutAttributes, 'showlegend', legendReallyHasATrace && legendTraceCount > 1); + // delete legend + if(showLegend === false) layoutOut.legend = undefined; + if(showLegend === false && !containerIn.uirevision) return; coerce('uirevision', layoutOut.uirevision); From 775b9fcfc3ebcd7b37257c9f4eee81c22d19f3b2 Mon Sep 17 00:00:00 2001 From: Mojtaba Samimi Date: Mon, 6 Dec 2021 16:36:52 -0500 Subject: [PATCH 7/7] add a jasmine test for hoverlabel.grouptitlefont --- test/jasmine/tests/hover_label_test.js | 34 ++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/test/jasmine/tests/hover_label_test.js b/test/jasmine/tests/hover_label_test.js index c8995911866..0461344a3f1 100644 --- a/test/jasmine/tests/hover_label_test.js +++ b/test/jasmine/tests/hover_label_test.js @@ -6144,6 +6144,40 @@ describe('hovermode: (x|y)unified', function() { .then(done, done.fail); }); + 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() { + var e = d3Select(this); + var text = e.select('text.legendtext'); + var node = text.node(); + var label = node.innerHTML; + 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'); + expect(textStyle.fill).toBe(fontColor, 'wrong font color'); + } + }); + } + + var mockCopy = Lib.extendDeep({}, groupTitlesMock); + + mockCopy.layout.hoverlabel = { + grouptitlefont: {size: 20, family: 'Mono', color: 'rgb(255, 127, 0)'} + }; + + Plotly.newPlot(gd, mockCopy) + .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) { var mockCopy = Lib.extendDeep({}, mock); mockCopy.data[0].hovertemplate = 'hovertemplate: %{y:0.2f}';