Skip to content

Commit 66fc665

Browse files
committed
Make pie font properties arrayOk [2951]
1 parent 66e26db commit 66fc665

File tree

3 files changed

+220
-11
lines changed

3 files changed

+220
-11
lines changed

src/traces/pie/attributes.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ var extendFlat = require('../../lib/extend').extendFlat;
1717

1818
var textFontAttrs = fontAttrs({
1919
editType: 'calc',
20+
arrayOk: true,
2021
colorEditType: 'style',
2122
description: 'Sets the font used for `textinfo`.'
2223
});
@@ -169,7 +170,6 @@ module.exports = {
169170
'Specifies the location of the `textinfo`.'
170171
].join(' ')
171172
},
172-
// TODO make those arrayOk?
173173
textfont: extendFlat({}, textFontAttrs, {
174174
description: 'Sets the font used for `textinfo`.'
175175
}),

src/traces/pie/plot.js

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -264,7 +264,8 @@ module.exports = function plot(gd, cdpie) {
264264
'text-anchor': 'middle'
265265
})
266266
.call(Drawing.font, textPosition === 'outside' ?
267-
trace.outsidetextfont : determineInsideTextFont(trace, pt))
267+
determineOutsideTextFont(trace, pt, gd._fullLayout.font) :
268+
determineInsideTextFont(trace, pt, gd._fullLayout.font))
268269
.call(svgTextUtils.convertToTspans, gd);
269270

270271
// position the text relative to the slice
@@ -409,6 +410,53 @@ module.exports = function plot(gd, cdpie) {
409410
}, 0);
410411
};
411412

413+
// TODO DRY?
414+
function determineOutsideTextFont(trace, pt, layoutFont) {
415+
var customColor = helpers.castOption(trace.outsidetextfont.color, pt.pts) ||
416+
helpers.castOption(trace.textfont.color, pt.pts) ||
417+
layoutFont.color;
418+
419+
var customFamily = helpers.castOption(trace.outsidetextfont.family, pt.pts) ||
420+
helpers.castOption(trace.textfont.family, pt.pts) ||
421+
layoutFont.family;
422+
423+
var customSize = helpers.castOption(trace.outsidetextfont.size, pt.pts) ||
424+
helpers.castOption(trace.textfont.size, pt.pts) ||
425+
layoutFont.size;
426+
427+
return {
428+
color: customColor,
429+
family: customFamily,
430+
size: customSize
431+
};
432+
}
433+
434+
function determineInsideTextFont(trace, pt, layoutFont) {
435+
var customColor = helpers.castOption(trace.insidetextfont.color, pt.pts);
436+
if(!customColor && trace._input.textfont) {
437+
438+
// Why not simply using trace.textfont? Because if not set, it
439+
// defaults to layout.font which has a default color. But if
440+
// textfont.color and insidetextfont.color don't supply a value,
441+
// a contrasting color shall be used.
442+
customColor = helpers.castOption(trace._input.textfont.color, pt.pts);
443+
}
444+
445+
var customFamily = helpers.castOption(trace.insidetextfont.family, pt.pts) ||
446+
helpers.castOption(trace.textfont.family, pt.pts) ||
447+
layoutFont.family;
448+
449+
var customSize = helpers.castOption(trace.insidetextfont.size, pt.pts) ||
450+
helpers.castOption(trace.textfont.size, pt.pts) ||
451+
layoutFont.size;
452+
453+
return {
454+
color: customColor || Color.contrast(pt.color),
455+
family: customFamily,
456+
size: customSize
457+
};
458+
}
459+
412460
function prerenderTitles(cdpie, gd) {
413461
var cd0, trace;
414462
// Determine the width and height of the title for each pie.
@@ -432,11 +480,6 @@ function prerenderTitles(cdpie, gd) {
432480
}
433481
}
434482

435-
function determineInsideTextFont(trace, pt) {
436-
var contrast = Color.contrast(pt.color);
437-
return Lib.extendFlat({}, { color: contrast }, trace.insidetextfont);
438-
}
439-
440483
function transformInsideText(textBB, pt, cd0) {
441484
var textDiameter = Math.sqrt(textBB.width * textBB.width + textBB.height * textBB.height);
442485
var textAspect = textBB.width / textBB.height;

test/jasmine/tests/pie_test.js

Lines changed: 170 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,8 @@ describe('Pie defaults', function() {
6868
expect(out.insidetextfont.color).toBe(undefined);
6969
});
7070

71-
it('does apply textfont.color to insidetextfont.color', function() {
72-
var out = _supply({type: 'pie', values: [1, 2], textfont: {color: 'blue'}});
71+
it('does apply textfont.color to insidetextfont.color if not set', function() {
72+
var out = _supply({type: 'pie', values: [1, 2], textfont: {color: 'blue'}}, {font: {color: 'red'}});
7373
expect(out.insidetextfont.color).toBe('blue');
7474
});
7575
});
@@ -168,7 +168,23 @@ describe('Pie traces', function() {
168168
function _checkFontColors(expFontColors) {
169169
return function() {
170170
d3.selectAll(SLICES_TEXT_SELECTOR).each(function(d, i) {
171-
expect(this.style.fill).toBe(expFontColors[i], i);
171+
expect(this.style.fill).toBe(expFontColors[i], 'fill color of ' + i);
172+
});
173+
};
174+
}
175+
176+
function _checkFontFamilies(expFontFamilies) {
177+
return function() {
178+
d3.selectAll(SLICES_TEXT_SELECTOR).each(function(d, i) {
179+
expect(this.style.fontFamily).toBe(expFontFamilies[i], 'fontFamily of ' + i);
180+
});
181+
};
182+
}
183+
184+
function _checkFontSizes(expFontSizes) {
185+
return function() {
186+
d3.selectAll(SLICES_TEXT_SELECTOR).each(function(d, i) {
187+
expect(this.style.fontSize).toBe(expFontSizes[i] + 'px', 'fontSize of ' + i);
172188
});
173189
};
174190
}
@@ -490,6 +506,35 @@ describe('Pie traces', function() {
490506
.then(done);
491507
});
492508

509+
[
510+
{fontAttr: 'textfont', textposition: 'outside'},
511+
{fontAttr: 'textfont', textposition: 'inside'},
512+
{fontAttr: 'outsidetextfont', textposition: 'outside'},
513+
{fontAttr: 'insidetextfont', textposition: 'inside'}
514+
].forEach(function(spec) {
515+
var desc = 'allow to specify ' + spec.fontAttr +
516+
' properties per individual slice (textposition ' + spec.textposition + ')';
517+
it(desc, function(done) {
518+
var data = {
519+
values: [3, 2, 1],
520+
type: 'pie',
521+
textposition: spec.textposition
522+
};
523+
data[spec.fontAttr] = {
524+
color: ['red', 'green', 'blue'],
525+
family: ['Arial', 'Gravitas', 'Roboto'],
526+
size: [12, 20, 16]
527+
};
528+
529+
Plotly.plot(gd, [data])
530+
.then(_checkFontColors([rgb('red'), rgb('green'), rgb('blue')]))
531+
.then(_checkFontFamilies(['Arial', 'Gravitas', 'Roboto']))
532+
.then(_checkFontSizes([12, 20, 16]))
533+
.catch(failTest)
534+
.then(done);
535+
});
536+
});
537+
493538
var insideTextTestsTraceDef = {
494539
values: [6, 5, 4, 3, 2, 1],
495540
type: 'pie',
@@ -506,19 +551,140 @@ describe('Pie traces', function() {
506551
});
507552

508553
it('should use textfont.color for inside text instead of the contrasting default', function(done) {
509-
var data = Lib.extendFlat({}, insideTextTestsTraceDef, { textfont: { color: 'red' } });
554+
var data = Lib.extendFlat({}, insideTextTestsTraceDef, {textfont: {color: 'red'}});
510555
Plotly.plot(gd, [data])
511556
.then(_checkFontColors(Lib.repeat(rgb('red'), 6)))
512557
.catch(failTest)
513558
.then(done);
514559
});
515560

561+
it('should use matching color from textfont.color array for inside text, contrasting otherwise', function(done) {
562+
var data = Lib.extendFlat({}, insideTextTestsTraceDef, {textfont: {color: ['red', 'blue']}});
563+
Plotly.plot(gd, [data])
564+
.then(_checkFontColors([rgb('red'), rgb('blue'), LIGHT, LIGHT, DARK, LIGHT]))
565+
.catch(failTest)
566+
.then(done);
567+
});
568+
516569
it('should not use layout.font.color for inside text, but a contrasting color instead', function(done) {
517570
Plotly.plot(gd, [insideTextTestsTraceDef], {font: {color: 'green'}})
518571
.then(_checkFontColors([DARK, DARK, LIGHT, LIGHT, DARK, LIGHT]))
519572
.catch(failTest)
520573
.then(done);
521574
});
575+
576+
it('should use matching color from insidetextfont.color array instead of the contrasting default', function(done) {
577+
var data = Lib.extendFlat({}, insideTextTestsTraceDef, {textfont: {color: ['orange', 'purple']}});
578+
Plotly.plot(gd, [data])
579+
.then(_checkFontColors([rgb('orange'), rgb('purple'), LIGHT, LIGHT, DARK, LIGHT]))
580+
.catch(failTest)
581+
.then(done);
582+
});
583+
584+
[
585+
{fontAttr: 'outsidetextfont', textposition: 'outside'},
586+
{fontAttr: 'insidetextfont', textposition: 'inside'}
587+
].forEach(function(spec) {
588+
it('should fall back to textfont scalar values if ' + spec.fontAttr + ' value ' +
589+
'arrays don\'t cover all slices', function(done) {
590+
var data = Lib.extendFlat({}, insideTextTestsTraceDef, {
591+
textposition: spec.textposition,
592+
textfont: {color: 'orange', family: 'Gravitas', size: 12}
593+
});
594+
data[spec.fontAttr] = {color: ['blue', 'yellow'], family: ['Arial', 'Arial'], size: [24, 34]};
595+
596+
var orange = rgb('orange');
597+
Plotly.plot(gd, [data])
598+
.then(_checkFontColors([rgb('blue'), rgb('yellow'), orange, orange, orange, orange]))
599+
.then(_checkFontFamilies(['Arial', 'Arial', 'Gravitas', 'Gravitas', 'Gravitas', 'Gravitas']))
600+
.then(_checkFontSizes([24, 34, 12, 12, 12, 12]))
601+
.catch(failTest)
602+
.then(done);
603+
});
604+
});
605+
606+
it('should fall back to textfont array values and layout.font scalar (except color)' +
607+
' values for inside text', function(done) {
608+
var layout = {font: {color: 'orange', family: 'serif', size: 16}};
609+
var data = Lib.extendFlat({}, insideTextTestsTraceDef, {
610+
textfont: {
611+
color: ['blue', 'blue'], family: ['Arial', 'Arial'], size: [18, 18]
612+
},
613+
insidetextfont: {
614+
color: ['purple'], family: ['Roboto'], size: [24]
615+
}
616+
});
617+
618+
Plotly.plot(gd, [data], layout)
619+
.then(_checkFontColors([rgb('purple'), rgb('blue'), LIGHT, LIGHT, DARK, LIGHT]))
620+
.then(_checkFontFamilies(['Roboto', 'Arial', 'serif', 'serif', 'serif', 'serif']))
621+
.then(_checkFontSizes([24, 18, 16, 16, 16, 16]))
622+
.catch(failTest)
623+
.then(done);
624+
});
625+
626+
it('should fall back to textfont array values and layout.font scalar' +
627+
' values for outside text', function(done) {
628+
var layout = {font: {color: 'orange', family: 'serif', size: 16}};
629+
var data = Lib.extendFlat({}, insideTextTestsTraceDef, {
630+
textposition: 'outside',
631+
textfont: {
632+
color: ['blue', 'blue'], family: ['Arial', 'Arial'], size: [18, 18]
633+
},
634+
outsidetextfont: {
635+
color: ['purple'], family: ['Roboto'], size: [24]
636+
}
637+
});
638+
639+
var orange = rgb('orange');
640+
Plotly.plot(gd, [data], layout)
641+
.then(_checkFontColors([rgb('purple'), rgb('blue'), orange, orange, orange, orange]))
642+
.then(_checkFontFamilies(['Roboto', 'Arial', 'serif', 'serif', 'serif', 'serif']))
643+
.then(_checkFontSizes([24, 18, 16, 16, 16, 16]))
644+
.catch(failTest)
645+
.then(done);
646+
});
647+
648+
[
649+
{fontAttr: 'textfont'},
650+
{fontAttr: 'insidetextfont'}
651+
].forEach(function(spec) {
652+
it('should fall back to layout.font scalar values for inside text (except color) if ' + spec.fontAttr + ' value ' +
653+
'arrays don\'t cover all slices', function(done) {
654+
var layout = {font: {color: 'orange', family: 'serif', size: 16}};
655+
var data = Lib.extendFlat({}, insideTextTestsTraceDef);
656+
data.textposition = 'inside';
657+
data[spec.fontAttr] = {color: ['blue', 'yellow'], family: ['Arial', 'Arial'], size: [24, 34]};
658+
659+
Plotly.plot(gd, [data], layout)
660+
.then(_checkFontColors([rgb('blue'), rgb('yellow'), LIGHT, LIGHT, DARK, LIGHT]))
661+
.then(_checkFontFamilies(['Arial', 'Arial', 'serif', 'serif', 'serif', 'serif']))
662+
.then(_checkFontSizes([24, 34, 16, 16, 16, 16]))
663+
.catch(failTest)
664+
.then(done);
665+
});
666+
});
667+
668+
[
669+
{fontAttr: 'textfont'},
670+
{fontAttr: 'outsidetextfont'}
671+
].forEach(function(spec) {
672+
it('should fall back to layout.font scalar values for outside text if ' + spec.fontAttr + ' value ' +
673+
'arrays don\'t cover all slices', function(done) {
674+
var layout = {font: {color: 'orange', family: 'serif', size: 16}};
675+
var data = Lib.extendFlat({}, insideTextTestsTraceDef);
676+
data.textposition = 'outside';
677+
data[spec.fontAttr] = {color: ['blue', 'yellow'], family: ['Arial', 'Arial'], size: [24, 34]};
678+
679+
var orange = rgb('orange');
680+
Plotly.plot(gd, [data], layout)
681+
.then(_checkFontColors([rgb('blue'), rgb('yellow'), orange, orange, orange, orange]))
682+
.then(_checkFontFamilies(['Arial', 'Arial', 'serif', 'serif', 'serif', 'serif']))
683+
.then(_checkFontSizes([24, 34, 16, 16, 16, 16]))
684+
.catch(failTest)
685+
.then(done);
686+
});
687+
});
522688
});
523689

524690
describe('pie hovering', function() {

0 commit comments

Comments
 (0)