Skip to content

texttemplate with date formatting #4071

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 43 commits into from
Aug 28, 2019
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
8deaed1
texttemplate: initial implementation
antoinerg Jul 22, 2019
ce3c9cc
move texttemplate_attributes.js from fx to plots
antoinerg Jul 23, 2019
ee0a309
do not export `templateFormatString`
antoinerg Jul 23, 2019
9330290
texttemplate: test that textinfo is not coerced when it's defined
antoinerg Jul 23, 2019
cf826dd
texttemplate: update description
antoinerg Jul 23, 2019
c3e9961
set texttemplate dflt back to ''
antoinerg Jul 23, 2019
e0bde45
texttemplate: add customdata variable and :lock: down with test
antoinerg Jul 23, 2019
902d2d3
texttemplate: add support for scatter(carpet|geo|polar|ternary)
antoinerg Jul 24, 2019
0b516f7
texttemplate: use result of appendArrayPointValue as available variables
antoinerg Jul 24, 2019
7d21680
texttemplate: fix scattergeo variable names
antoinerg Jul 24, 2019
7fa4dc5
texttemplate: add support for scattergl
antoinerg Jul 24, 2019
aeb6d1f
texttemplate: add support for scatterpolargl
antoinerg Aug 6, 2019
f9062e4
texttemplate: add support for scatter3d
antoinerg Aug 6, 2019
3ad30ab
Merge branch 'master' into texttemplate
antoinerg Aug 6, 2019
2d50269
Merge branch 'texttemplate-gl' into texttemplate
antoinerg Aug 6, 2019
05f4722
check_texttemplate: put @gl tests in its own `it` block
antoinerg Aug 6, 2019
6729286
split gl2d and gl3d in separate mocks
antoinerg Aug 6, 2019
cbbcfcb
texttemplate: use d3locale in GL traces, fix style
antoinerg Aug 7, 2019
11ea019
Merge branch 'texttemplate-split' into texttemplate
antoinerg Aug 7, 2019
32498f8
remove unused baseline `texttemplate_2`
antoinerg Aug 7, 2019
7bb3379
texttemplate: text in legends should not changed
antoinerg Aug 13, 2019
9677929
texttemplate: fix GL traces with txt array longer than data
antoinerg Aug 13, 2019
b9188a9
Merge branch 'master' into texttemplate
antoinerg Aug 13, 2019
decec45
scattergl: :lock: down handling of texttemplate with fewer elements t…
antoinerg Aug 13, 2019
2f6bf8a
scattergl: make sure text(template) is not mutated
antoinerg Aug 13, 2019
e7c034a
texttemplate: test that trace.meta is passed to template
antoinerg Aug 14, 2019
4b1face
texttemplate: pass trace._meta to gl2d traces
antoinerg Aug 14, 2019
2528aa6
Merge branch 'master' into texttemplate
antoinerg Aug 14, 2019
e381c25
Merge branch 'texttemplate-meta' into texttemplate
antoinerg Aug 14, 2019
888cf68
texttemplate: DRY code for gl(2|3)d traces, pass meta to scatter3d
antoinerg Aug 14, 2019
7d3fabb
texttemplate: add support for scattermapbox
antoinerg Aug 7, 2019
b2df623
funnelarea: fix description of variables in (hover|text)template
antoinerg Aug 20, 2019
8c339df
funnel(area): fix description of variables for (hover|text)template
antoinerg Aug 21, 2019
c6370d9
support formatting date in (hover|text)template
antoinerg Aug 19, 2019
836d5a2
templateFormatString: use d3locale.timeFormat if available fot date fmt
antoinerg Aug 20, 2019
3dda912
texttemplate for scattermapbox: fix editType, improve style and mock
antoinerg Aug 26, 2019
354b70f
fix baseline "mapbox_texttemplate"
antoinerg Aug 27, 2019
4ef8392
merge (text|hover)templateAttrs() into a single file
antoinerg Aug 27, 2019
e700fc2
texttemplate: fix style
antoinerg Aug 27, 2019
6780a3d
texttemplate: do not coerce texttemplate when no text is displayed
antoinerg Aug 28, 2019
db87e62
(hover|text)template: pie/funnel/waterfall fix attribute definition
antoinerg Aug 28, 2019
daa2c7e
templateFormatString: do not error out when lookup objects are undefined
antoinerg Aug 28, 2019
c7dd845
don't coerce texttemplate when no text - fix for scatterternary
etpinard Aug 28, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion src/components/drawing/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -689,15 +689,20 @@ drawing.textPointStyle = function(s, trace, gd) {
selectedTextColorFn = fns.selectedTextColorFn;
}

var template = trace.texttemplate;
s.each(function(d) {
var p = d3.select(this);
var text = Lib.extractOption(d, trace, 'tx', 'text');
var text = Lib.extractOption(d, trace, template ? 'txt' : 'tx', template ? 'texttemplate' : 'text');

if(!text && text !== 0) {
p.remove();
return;
}

if(template) {
text = Lib.texttemplateString(text, {}, gd._fullLayout._d3locale, d, trace._meta || {});
}

var pos = d.tp || trace.textposition;
var fontSize = extracTextFontSize(d, trace);
var fontColor = selectedTextColorFn ?
Expand Down
41 changes: 30 additions & 11 deletions src/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -993,9 +993,25 @@ lib.templateString = function(string, obj) {
});
};

var hovertemplateWarnings = {
max: 10,
count: 0,
name: 'hovertemplate'
};
lib.hovertemplateString = function() {
return templateFormatString.apply(hovertemplateWarnings, arguments);
};

var texttemplateWarnings = {
max: 10,
count: 0,
name: 'texttemplate'
};
lib.texttemplateString = function() {
return templateFormatString.apply(texttemplateWarnings, arguments);
};

var TEMPLATE_STRING_FORMAT_SEPARATOR = /^:/;
var numberOfHoverTemplateWarnings = 0;
var maximumNumberOfHoverTemplateWarnings = 10;
/**
* Substitute values from an object into a string and optionally formats them using d3-format,
* or fallback to associated labels.
Expand All @@ -1005,14 +1021,15 @@ var maximumNumberOfHoverTemplateWarnings = 10;
* Lib.hovertemplateString('name: %{trace[0].name}', {trace: [{name: 'asdf'}]}) --> 'name: asdf'
* Lib.hovertemplateString('price: %{y:$.2f}', {y: 1}) --> 'price: $1.00'
*
* @param {obj} d3 locale
* @param {string} input string containing %{...:...} template strings
* @param {obj} data object containing fallback text when no formatting is specified, ex.: {yLabel: 'formattedYValue'}
* @param {obj} d3 locale
* @param {obj} data objects containing substitution values
*
* @return {string} templated string
*/
lib.hovertemplateString = function(string, labels, d3locale) {
function templateFormatString(string, labels, d3locale) {
var opts = this;
var args = arguments;
// Not all that useful, but cache nestedProperty instantiation
// just in case it speeds things up *slightly*:
Expand All @@ -1034,16 +1051,18 @@ lib.hovertemplateString = function(string, labels, d3locale) {
if(value !== undefined) break;
}

if(value === undefined) {
if(numberOfHoverTemplateWarnings < maximumNumberOfHoverTemplateWarnings) {
lib.warn('Variable \'' + key + '\' in hovertemplate could not be found!');
if(value === undefined && opts) {
if(opts.count < opts.max) {
lib.warn('Variable \'' + key + '\' in ' + opts.name + ' could not be found!');
value = match;
}

if(numberOfHoverTemplateWarnings === maximumNumberOfHoverTemplateWarnings) {
lib.warn('Too many hovertemplate warnings - additional warnings will be suppressed');
if(opts.count === opts.max) {
lib.warn('Too many ' + opts.name + ' warnings - additional warnings will be suppressed');
}
numberOfHoverTemplateWarnings++;
opts.count++;

return match;
}

if(format) {
Expand All @@ -1059,7 +1078,7 @@ lib.hovertemplateString = function(string, labels, d3locale) {
}
return value;
});
};
}

/*
* alphanumeric string sort, tailored for subplot IDs like scene2, scene10, x10y13 etc
Expand Down
52 changes: 52 additions & 0 deletions src/plots/texttemplate_attributes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* Copyright 2012-2019, Plotly, Inc.
* All rights reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

'use strict';

var FORMAT_LINK = require('../constants/docs').FORMAT_LINK;

module.exports = function(opts, extra) {
opts = opts || {};
extra = extra || {};

var descPart = extra.description ? ' ' + extra.description : '';
var keys = extra.keys || [];
if(keys.length > 0) {
var quotedKeys = [];
for(var i = 0; i < keys.length; i++) {
quotedKeys[i] = '`' + keys[i] + '`';
}
descPart = descPart + 'Finally, the template string has access to ';
if(keys.length === 1) {
descPart = 'variable ' + quotedKeys[0];
} else {
descPart = 'variables ' + quotedKeys.slice(0, -1).join(', ') + ' and ' + quotedKeys.slice(-1) + '.';
}
}

var texttemplate = {
valType: 'string',
role: 'info',
editType: opts.editType || 'calc',
description: [
'Template string used for rendering the information that appear on points.',
'Note that this will override `textinfo`.',
'Variables are inserted using %{variable}, for example "y: %{y}".',
'Numbers are formatted using d3-format\'s syntax %{variable:d3-format}, for example "Price: %{y:$.2f}".',
FORMAT_LINK,
'for details on the formatting syntax.',
'Every attributes that can be specified per-point (the ones that are `arrayOk: true`) are available.',
descPart
].join(' ')
};

if(opts.arrayOk !== false) {
texttemplate.arrayOk = true;
}
return texttemplate;
};
4 changes: 4 additions & 0 deletions src/traces/bar/attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

var scatterAttrs = require('../scatter/attributes');
var hovertemplateAttrs = require('../../components/fx/hovertemplate_attributes');
var texttemplateAttrs = require('../../plots/texttemplate_attributes');
var colorScaleAttrs = require('../../components/colorscale/attributes');
var fontAttrs = require('../../plots/font_attributes');
var constants = require('./constants.js');
Expand Down Expand Up @@ -59,6 +60,9 @@ module.exports = {
dy: scatterAttrs.dy,

text: scatterAttrs.text,
texttemplate: texttemplateAttrs({editType: 'plot'}, {
keys: constants.eventDataKeys
}),
hovertext: scatterAttrs.hovertext,
hovertemplate: hovertemplateAttrs({}, {
keys: constants.eventDataKeys
Expand Down
1 change: 1 addition & 0 deletions src/traces/bar/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
coerce('width');

coerce('text');
coerce('texttemplate');
coerce('hovertext');
coerce('hovertemplate');

Expand Down
71 changes: 66 additions & 5 deletions src/traces/bar/plot.js
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ function appendBarText(gd, plotinfo, bar, calcTrace, i, x0, x1, y0, y1, opts) {
var trace = calcTrace[0].trace;
var isHorizontal = (trace.orientation === 'h');

var text = getText(calcTrace, i, xa, ya);
var text = getText(fullLayout, calcTrace, i, xa, ya);
textPosition = getTextPosition(trace, i);

// compute text position
Expand Down Expand Up @@ -516,14 +516,17 @@ function getTransform(textX, textY, targetX, targetY, scale, rotation) {
return transformTranslate + transformScale + transformRotate;
}

function getText(calcTrace, index, xa, ya) {
function getText(fullLayout, calcTrace, index, xa, ya) {
var trace = calcTrace[0].trace;
var texttemplate = trace.texttemplate;

var value;
if(!trace.textinfo) {
value = helpers.getValue(trace.text, index);
} else {
if(texttemplate) {
value = calcTexttemplate(fullLayout, calcTrace, index, xa, ya);
} else if(trace.textinfo) {
value = calcTextinfo(calcTrace, index, xa, ya);
} else {
value = helpers.getValue(trace.text, index);
}

return helpers.coerceString(attributeText, value);
Expand All @@ -534,6 +537,64 @@ function getTextPosition(trace, index) {
return helpers.coerceEnumerated(attributeTextPosition, value);
}

function calcTexttemplate(fullLayout, calcTrace, index, xa, ya) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to merge this routine with calcTextinfo where we could first set the obj.??Label values and then use those to translate the textinfo flags.

var trace = calcTrace[0].trace;
var texttemplate = Lib.castOption(trace, index, 'texttemplate');
if(!texttemplate) return '';
var isHorizontal = (trace.orientation === 'h');
var isWaterfall = (trace.type === 'waterfall');
var isFunnel = (trace.type === 'funnel');

function formatLabel(u) {
var pAxis = isHorizontal ? ya : xa;
return tickText(pAxis, u, true).text;
}

function formatNumber(v) {
var sAxis = isHorizontal ? xa : ya;
return tickText(sAxis, +v, true).text;
}

var cdi = calcTrace[index];
var obj = {};

obj.label = cdi.p;
obj.labelLabel = formatLabel(cdi.p);

var tx = Lib.castOption(trace, cdi.i, 'text');
if(tx === 0 || tx) obj.text = tx;

obj.value = cdi.s;
obj.valueLabel = formatNumber(cdi.s);

obj.x = cdi.x;
obj.y = cdi.y;

if(isWaterfall) {
obj.delta = +cdi.rawS || cdi.s;
obj.deltaLabel = formatNumber(obj.delta);
obj.final = cdi.v;
obj.finalLabel = formatNumber(obj.final);
obj.initial = obj.final - obj.delta;
obj.initialLabel = formatNumber(obj.initial);
}

if(isFunnel) {
obj.value = cdi.s;
obj.valueLabel = formatNumber(obj.value);

obj.percentInitial = cdi.begR;
obj.percentInitialLabel = Lib.formatPercent(cdi.begR);
obj.percentPrevious = cdi.difR;
obj.percentPreviousLabel = Lib.formatPercent(cdi.difR);
obj.percentTotal = cdi.sumR;
obj.percenTotalLabel = Lib.formatPercent(cdi.sumR);
}

return Lib.texttemplateString(texttemplate, obj, fullLayout._d3locale, obj, trace._meta || {});
}

// TODO: calcTextinfo should build a texttemplate pass it to calcTexttemplate()
function calcTextinfo(calcTrace, index, xa, ya) {
var trace = calcTrace[0].trace;
var isHorizontal = (trace.orientation === 'h');
Expand Down
4 changes: 4 additions & 0 deletions src/traces/funnel/attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ var barAttrs = require('../bar/attributes');
var lineAttrs = require('../scatter/attributes').line;
var plotAttrs = require('../../plots/attributes');
var hovertemplateAttrs = require('../../components/fx/hovertemplate_attributes');
var texttemplateAttrs = require('../../plots/texttemplate_attributes');
var constants = require('./constants');
var extendFlat = require('../../lib/extend').extendFlat;
var Color = require('../../components/color');
Expand Down Expand Up @@ -46,6 +47,9 @@ module.exports = {
'are computed separately (per trace).'
].join(' ')
},
texttemplate: texttemplateAttrs({editType: 'plot'}, {
keys: constants.eventDataKeys
}),

text: barAttrs.text,
textposition: extendFlat({}, barAttrs.textposition, {dflt: 'auto'}),
Expand Down
3 changes: 2 additions & 1 deletion src/traces/funnel/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
coerce('width');

var text = coerce('text');
coerce('texttemplate');

coerce('hovertext');
coerce('hovertemplate');
Expand All @@ -46,7 +47,7 @@ function supplyDefaults(traceIn, traceOut, defaultColor, layout) {
moduleHasInsideanchor: true
});

if(traceOut.textposition !== 'none') {
if(traceOut.textposition !== 'none' && !traceOut.texttemplate) {
coerce('textinfo', Array.isArray(text) ? 'text+value' : 'value');
}

Expand Down
5 changes: 5 additions & 0 deletions src/traces/funnelarea/attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ var pieAttrs = require('../pie/attributes');
var plotAttrs = require('../../plots/attributes');
var domainAttrs = require('../../plots/domain').attributes;
var hovertemplateAttrs = require('../../components/fx/hovertemplate_attributes');
var texttemplateAttrs = require('../../plots/texttemplate_attributes');

var extendFlat = require('../../lib/extend').extendFlat;

Expand Down Expand Up @@ -53,6 +54,10 @@ module.exports = {
flags: ['label', 'text', 'value', 'percent']
}),

texttemplate: texttemplateAttrs({editType: 'plot'}, {
keys: ['label', 'color', 'value', 'percent', 'text']
}),

hoverinfo: extendFlat({}, plotAttrs.hoverinfo, {
flags: ['label', 'text', 'value', 'percent', 'name']
}),
Expand Down
7 changes: 5 additions & 2 deletions src/traces/funnelarea/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,11 +46,14 @@ module.exports = function supplyDefaults(traceIn, traceOut, defaultColor, layout
coerce('scalegroup');

var textData = coerce('text');
var textInfo = coerce('textinfo', Array.isArray(textData) ? 'text+percent' : 'percent');
var textTemplate = coerce('texttemplate');
var textInfo;
if(!textTemplate) textInfo = coerce('textinfo', Array.isArray(textData) ? 'text+percent' : 'percent');

coerce('hovertext');
coerce('hovertemplate');

if(textInfo && textInfo !== 'none') {
if(textTemplate || (textInfo && textInfo !== 'none')) {
var textposition = coerce('textposition');
handleText(traceIn, traceOut, layout, coerce, textposition, {
moduleHasSelected: false,
Expand Down
1 change: 1 addition & 0 deletions src/traces/funnelarea/plot.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ module.exports = function plot(gd, cdModule) {
slicePath.attr('d', shape);

// add text
piePlot.formatSliceLabel(gd, pt, cd0);
var textPosition = pieHelpers.castOption(trace.textposition, pt.pts);
var sliceTextGroup = sliceTop.selectAll('g.slicetext')
.data(pt.text && (textPosition !== 'none') ? [0] : []);
Expand Down
4 changes: 4 additions & 0 deletions src/traces/pie/attributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ var domainAttrs = require('../../plots/domain').attributes;
var fontAttrs = require('../../plots/font_attributes');
var colorAttrs = require('../../components/color/attributes');
var hovertemplateAttrs = require('../../components/fx/hovertemplate_attributes');
var texttemplateAttrs = require('../../plots/texttemplate_attributes');

var extendFlat = require('../../lib/extend').extendFlat;

Expand Down Expand Up @@ -163,6 +164,9 @@ module.exports = {
hovertemplate: hovertemplateAttrs({}, {
keys: ['label', 'color', 'value', 'percent', 'text']
}),
texttemplate: texttemplateAttrs({editType: 'plot'}, {
keys: ['label', 'color', 'value', 'percent', 'text']
}),
textposition: {
valType: 'enumerated',
role: 'info',
Expand Down
Loading