Skip to content

Commit 3eaa3b9

Browse files
committed
replace templateString with the more specific hovertemplateString
1 parent 040208f commit 3eaa3b9

File tree

3 files changed

+73
-16
lines changed

3 files changed

+73
-16
lines changed

src/components/fx/hover.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -955,7 +955,7 @@ function createHoverText(hoverData, opts, gd) {
955955
// hovertemplate
956956
var trace = d.trace, hovertemplate = opts.hovertemplate || trace.hovertemplate || false;
957957
if(hovertemplate) {
958-
text = Lib.templateString(hovertemplate, gd._hoverdata[curveNumber], trace);
958+
text = Lib.hovertemplateString(hovertemplate, d, gd._hoverdata[curveNumber], trace);
959959
}
960960

961961
// main label

src/lib/index.js

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -982,28 +982,57 @@ var TEMPLATE_STRING_REGEX = /%{([^\s%{}:]*)(:[^}]*)?}/g;
982982
var SIMPLE_PROPERTY_REGEX = /^\w*$/;
983983

984984
/*
985-
* Substitute values from an object into a string and optionally formats them using d3-format
985+
* Substitute values from an object into a string
986+
*
987+
* Examples:
988+
* Lib.templateString('name: %{trace}', {trace: 'asdf'}) --> 'name: asdf'
989+
* Lib.templateString('name: %{trace[0].name}', {trace: [{name: 'asdf'}]}) --> 'name: asdf'
990+
*
991+
* @param {string} input string containing %{...} template strings
992+
* @param {obj} data object containing substitution values
993+
*
994+
* @return {string} templated string
995+
*/
996+
997+
lib.templateString = function(string, obj) {
998+
// Not all that useful, but cache nestedProperty instantiation
999+
// just in case it speeds things up *slightly*:
1000+
var getterCache = {};
1001+
1002+
return string.replace(TEMPLATE_STRING_REGEX, function(dummy, key) {
1003+
if(SIMPLE_PROPERTY_REGEX.test(key)) {
1004+
return obj[key] || '';
1005+
}
1006+
getterCache[key] = getterCache[key] || lib.nestedProperty(obj, key).get;
1007+
return getterCache[key]() || '';
1008+
});
1009+
};
1010+
1011+
/*
1012+
* Substitute values from an object into a string and optionally formats them using d3-format,
1013+
* or fallback to associated labels.
9861014
*
9871015
* Examples:
9881016
* Lib.templateString('name: %{trace}', {trace: 'asdf'}) --> 'name: asdf'
9891017
* Lib.templateString('name: %{trace[0].name}', {trace: [{name: 'asdf'}]}) --> 'name: asdf'
9901018
* Lib.templateString('price: %{y:$.2f}', {y: 1}) --> 'price: $1.00'
9911019
*
9921020
* @param {string} input string containing %{...:...} template strings
1021+
* @param {obj} data object containing fallback text when no formatting is specified, ex.: {yLabel: 'formattedYValue'}
9931022
* @param {obj} data objects containing substitution values
9941023
*
9951024
* @return {string} templated string
9961025
*/
9971026

998-
lib.templateString = function(string) {
1027+
lib.hovertemplateString = function(string, labels) {
9991028
var args = arguments;
10001029
// Not all that useful, but cache nestedProperty instantiation
10011030
// just in case it speeds things up *slightly*:
10021031
var getterCache = {};
10031032

1004-
return string.replace(TEMPLATE_STRING_REGEX, function(dummy, key, format) {
1033+
return string.replace(TEMPLATE_STRING_REGEX, function(match, key, format) {
10051034
var obj, value, i;
1006-
for(i = 1; i < args.length; i++) {
1035+
for(i = 2; i < args.length; i++) {
10071036
obj = args[i];
10081037
if(obj.hasOwnProperty(key)) {
10091038
value = obj[key];
@@ -1019,10 +1048,12 @@ lib.templateString = function(string) {
10191048
if(value !== undefined) break;
10201049
}
10211050

1022-
if(value === undefined) value = '';
1051+
if(value === undefined) value = match;
10231052

10241053
if(format) {
10251054
value = d3.format(format.replace(/^:/, ''))(value);
1055+
} else {
1056+
if(labels.hasOwnProperty(key + 'Label')) value = labels[key + 'Label'];
10261057
}
10271058
return value;
10281059
});

test/jasmine/tests/lib_test.js

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2124,10 +2124,6 @@ describe('Test lib.js:', function() {
21242124
expect(Lib.templateString('foo %{bar}', {bar: 'baz'})).toEqual('foo baz');
21252125
});
21262126

2127-
it('evaluates attributes with a dot in their name', function() {
2128-
expect(Lib.templateString('%{marker.size}', {'marker.size': 12}, {marker: {size: 14}})).toEqual('12');
2129-
});
2130-
21312127
it('evaluates nested properties', function() {
21322128
expect(Lib.templateString('foo %{bar.baz}', {bar: {baz: 'asdf'}})).toEqual('foo asdf');
21332129
});
@@ -2147,24 +2143,54 @@ describe('Test lib.js:', function() {
21472143
it('replaces empty key with empty string', function() {
21482144
expect(Lib.templateString('foo %{} %{}', {})).toEqual('foo ');
21492145
});
2146+
});
2147+
2148+
describe('hovertemplateString', function() {
2149+
it('evaluates attributes', function() {
2150+
expect(Lib.hovertemplateString('foo %{bar}', {}, {bar: 'baz'})).toEqual('foo baz');
2151+
});
2152+
2153+
it('evaluates attributes with a dot in their name', function() {
2154+
expect(Lib.hovertemplateString('%{marker.size}', {}, {'marker.size': 12}, {marker: {size: 14}})).toEqual('12');
2155+
});
2156+
2157+
it('evaluates nested properties', function() {
2158+
expect(Lib.hovertemplateString('foo %{bar.baz}', {}, {bar: {baz: 'asdf'}})).toEqual('foo asdf');
2159+
});
2160+
2161+
it('evaluates array nested properties', function() {
2162+
expect(Lib.hovertemplateString('foo %{bar[0].baz}', {}, {bar: [{baz: 'asdf'}]})).toEqual('foo asdf');
2163+
});
2164+
2165+
it('subtitutes multiple matches', function() {
2166+
expect(Lib.hovertemplateString('foo %{group} %{trace}', {}, {group: 'asdf', trace: 'jkl;'})).toEqual('foo asdf jkl;');
2167+
});
2168+
2169+
it('replaces missing matches with template string', function() {
2170+
expect(Lib.hovertemplateString('foo %{group} %{trace}', {}, {group: 1})).toEqual('foo 1 %{trace}');
2171+
});
21502172

21512173
it('uses the value from the first object with the specified key', function() {
21522174
var obj1 = {a: 'first'}, obj2 = {a: 'second', foo: {bar: 'bar'}};
21532175

21542176
// Simple key
2155-
expect(Lib.templateString('foo %{a}', obj1, obj2)).toEqual('foo first');
2156-
expect(Lib.templateString('foo %{a}', obj2, obj1)).toEqual('foo second');
2177+
expect(Lib.hovertemplateString('foo %{a}', {}, obj1, obj2)).toEqual('foo first');
2178+
expect(Lib.hovertemplateString('foo %{a}', {}, obj2, obj1)).toEqual('foo second');
21572179

21582180
// Nested Keys
2159-
expect(Lib.templateString('foo %{foo.bar}', obj1, obj2)).toEqual('foo bar');
2181+
expect(Lib.hovertemplateString('foo %{foo.bar}', {}, obj1, obj2)).toEqual('foo bar');
21602182

21612183
// Nested keys with 0
2162-
expect(Lib.templateString('y: %{y}', {y: 0}, {y: 1})).toEqual('y: 0');
2184+
expect(Lib.hovertemplateString('y: %{y}', {}, {y: 0}, {y: 1})).toEqual('y: 0');
21632185
});
21642186

21652187
it('formats value using d3 mini-language', function() {
2166-
expect(Lib.templateString('a: %{a:.0%}', {a: 0.123})).toEqual('a: 12%');
2167-
expect(Lib.templateString('b: %{b:2.2f}', {b: 43})).toEqual('b: 43.00');
2188+
expect(Lib.hovertemplateString('a: %{a:.0%}', {}, {a: 0.123})).toEqual('a: 12%');
2189+
expect(Lib.hovertemplateString('b: %{b:2.2f}', {}, {b: 43})).toEqual('b: 43.00');
2190+
});
2191+
2192+
it('looks for default label if no format is provided', function() {
2193+
expect(Lib.hovertemplateString('y: %{y}', {yLabel: '0.1'}, {y: 0.123})).toEqual('y: 0.1');
21682194
});
21692195
});
21702196

0 commit comments

Comments
 (0)