Skip to content

Commit 08da201

Browse files
authored
Merge pull request #7360 from plotly/alex/template-null
Allow null and NaN in text templates
2 parents 93f761f + a348c24 commit 08da201

File tree

4 files changed

+40
-14
lines changed

4 files changed

+40
-14
lines changed

Diff for: draftlogs/7360_fix.md

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- Fix hoverlabels and other text labels with null values templated in [[#7360](https://github.com/plotly/plotly.js/pull/7360)]

Diff for: src/lib/index.js

+4-8
Original file line numberDiff line numberDiff line change
@@ -1067,9 +1067,9 @@ lib.templateString = function(string, obj) {
10671067
v = obj[key];
10681068
} else {
10691069
getterCache[key] = getterCache[key] || lib.nestedProperty(obj, key).get;
1070-
v = getterCache[key]();
1070+
v = getterCache[key](true); // true means don't replace undefined with null
10711071
}
1072-
return lib.isValidTextValue(v) ? v : '';
1072+
return (v !== undefined) ? v : '';
10731073
});
10741074
};
10751075

@@ -1132,9 +1132,6 @@ function templateFormatString(string, labels, d3locale) {
11321132
var opts = this;
11331133
var args = arguments;
11341134
if(!labels) labels = {};
1135-
// Not all that useful, but cache nestedProperty instantiation
1136-
// just in case it speeds things up *slightly*:
1137-
var getterCache = {};
11381135

11391136
return string.replace(lib.TEMPLATE_STRING_REGEX, function(match, rawKey, format) {
11401137
var isOther =
@@ -1185,9 +1182,8 @@ function templateFormatString(string, labels, d3locale) {
11851182
}
11861183

11871184
if(!SIMPLE_PROPERTY_REGEX.test(key)) {
1188-
value = lib.nestedProperty(obj, key).get();
1189-
value = getterCache[key] || lib.nestedProperty(obj, key).get();
1190-
if(value) getterCache[key] = value;
1185+
// true here means don't convert null to undefined
1186+
value = lib.nestedProperty(obj, key).get(true);
11911187
}
11921188
if(value !== undefined) break;
11931189
}

Diff for: src/lib/nested_property.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ module.exports = function nestedProperty(container, propStr) {
7373
};
7474

7575
function npGet(cont, parts) {
76-
return function() {
76+
return function(retainNull) {
7777
var curCont = cont;
7878
var curPart;
7979
var allSame;
@@ -87,7 +87,7 @@ function npGet(cont, parts) {
8787
allSame = true;
8888
out = [];
8989
for(j = 0; j < curCont.length; j++) {
90-
out[j] = npGet(curCont[j], parts.slice(i + 1))();
90+
out[j] = npGet(curCont[j], parts.slice(i + 1))(retainNull);
9191
if(out[j] !== out[0]) allSame = false;
9292
}
9393
return allSame ? out[0] : out;
@@ -105,7 +105,7 @@ function npGet(cont, parts) {
105105
if(typeof curCont !== 'object' || curCont === null) return undefined;
106106

107107
out = curCont[parts[i]];
108-
if(out === null) return undefined;
108+
if(!retainNull && (out === null)) return undefined;
109109
return out;
110110
};
111111
}

Diff for: test/jasmine/tests/lib_test.js

+32-3
Original file line numberDiff line numberDiff line change
@@ -340,11 +340,12 @@ describe('Test lib.js:', function() {
340340
});
341341

342342
it('should access properties of objects in an array with index -1', function() {
343-
var obj = {arr: [{a: 1}, {a: 2}, {b: 3}]};
343+
var obj = {arr: [{a: 1}, {a: null}, {b: 3}]};
344344
var prop = np(obj, 'arr[-1].a');
345345

346-
expect(prop.get()).toEqual([1, 2, undefined]);
347-
expect(obj).toEqual({arr: [{a: 1}, {a: 2}, {b: 3}]});
346+
expect(prop.get()).toEqual([1, undefined, undefined]);
347+
expect(prop.get(true)).toEqual([1, null, undefined]);
348+
expect(obj).toEqual({arr: [{a: 1}, {a: null}, {b: 3}]});
348349

349350
prop.set(5);
350351
expect(prop.get()).toBe(5);
@@ -2443,6 +2444,14 @@ describe('Test lib.js:', function() {
24432444
it('should work with the number *0* (nested case)', function() {
24442445
expect(Lib.templateString('%{x.y}', {x: {y: 0}})).toEqual('0');
24452446
});
2447+
2448+
it('preserves null and NaN', function() {
2449+
expect(Lib.templateString(
2450+
'%{a} %{b} %{c.d} %{c.e} %{f[0]} %{f[1]}',
2451+
{a: null, b: NaN, c: {d: null, e: NaN}, f: [null, NaN]}
2452+
))
2453+
.toEqual('null NaN null NaN null NaN');
2454+
});
24462455
});
24472456

24482457
describe('hovertemplateString', function() {
@@ -2471,6 +2480,16 @@ describe('Test lib.js:', function() {
24712480
expect(Lib.hovertemplateString('%{x.y}', {}, locale, {x: {y: 0}})).toEqual('0');
24722481
});
24732482

2483+
it('preserves null and NaN', function() {
2484+
expect(Lib.hovertemplateString(
2485+
'%{a} %{b} %{c.d} %{c.e} %{f[0]} %{f[1]}',
2486+
{},
2487+
locale,
2488+
{a: null, b: NaN, c: {d: null, e: NaN}, f: [null, NaN]}
2489+
))
2490+
.toEqual('null NaN null NaN null NaN');
2491+
});
2492+
24742493
it('subtitutes multiple matches', function() {
24752494
expect(Lib.hovertemplateString('foo %{group} %{trace}', {}, locale, {group: 'asdf', trace: 'jkl;'})).toEqual('foo asdf jkl;');
24762495
});
@@ -2537,6 +2556,16 @@ describe('Test lib.js:', function() {
25372556
expect(Lib.texttemplateString('y: %{y}', {yLabel: '0.1'}, locale, {y: 0.123})).toEqual('y: 0.1');
25382557
});
25392558

2559+
it('preserves null and NaN', function() {
2560+
expect(Lib.texttemplateString(
2561+
'%{a} %{b} %{c.d} %{c.e} %{f[0]} %{f[1]}',
2562+
{},
2563+
locale,
2564+
{a: null, b: NaN, c: {d: null, e: NaN}, f: [null, NaN]}
2565+
))
2566+
.toEqual('null NaN null NaN null NaN');
2567+
});
2568+
25402569
it('warns user up to 10 times if a variable cannot be found', function() {
25412570
spyOn(Lib, 'warn').and.callThrough();
25422571
Lib.texttemplateString('%{idontexist}', {});

0 commit comments

Comments
 (0)