The title plugin and scale title now accept lineHeight specified using unitless value (1.4), length ('1.4em' or '12px'), percentage ('200%') or keyword ('normal' === 1.2). The line height parsing has been refactored under the 'Chart.helpers.options' namespace. Also fix incorrect text positioning in the title plugin.
https://developer.mozilla.org/en-US/docs/Web/CSS/line-height
| -----| ---- | --------| -----------
| `display` | `Boolean` | `false` | If true, display the axis title.
| `labelString` | `String` | `''` | The text for the title. (i.e. "# of People" or "Response Choices").
-| `lineHeight` | `Number` | `` | Height of an individual line of text. If not defined, the font size is used.
+| `lineHeight` | `Number|String` | `1.2` | Height of an individual line of text (see [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/line-height))
| `fontColor` | Color | `'#666'` | Font color for scale title.
| `fontFamily` | `String` | `"'Helvetica Neue', 'Helvetica', 'Arial', sans-serif"` | Font family for the scale title, follows CSS font-family options.
| `fontSize` | `Number` | `12` | Font size for scale title.
| `fontColor` | Color | `'#666'` | Font color
| `fontStyle` | `String` | `'bold'` | Font style
| `padding` | `Number` | `10` | Number of pixels to add above and below the title text.
-| `lineHeight` | `Number` | `undefined` | Height of line of text. If not specified, the `fontSize` is used.
+| `lineHeight` | `Number|String` | `1.2` | Height of an individual line of text (see [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/line-height))
| `text` | `String/String[]` | `''` | Title text to display. If specified as an array, text is rendered on multiple lines.
### Position
// scale label
scaleLabel: {
+ // display property
+ display: false,
+
// actual label
labelString: '',
- // display property
- display: false,
+ lineHeight: 1.2
},
// label settings
};
}
+ function parseLineHeight(options) {
+ return helpers.options.toLineHeight(
+ helpers.valueOrDefault(options.lineHeight, 1.2),
+ helpers.valueOrDefault(options.fontSize, defaults.global.defaultFontSize));
+ }
+
Chart.Scale = Chart.Element.extend({
/**
* Get the padding needed for the scale
var isHorizontal = me.isHorizontal();
var tickFont = parseFontOptions(tickOpts);
- var scaleLabelLineHeight = helpers.valueOrDefault(scaleLabelOpts.lineHeight, parseFontOptions(scaleLabelOpts).size * 1.5);
var tickMarkLength = opts.gridLines.tickMarkLength;
+ var scaleLabelLineHeight = parseLineHeight(scaleLabelOpts);
// Width
if (isHorizontal) {
var scaleLabelX;
var scaleLabelY;
var rotation = 0;
- var halfLineHeight = helpers.valueOrDefault(scaleLabel.lineHeight, scaleLabelFont.size) / 2;
+ var halfLineHeight = parseLineHeight(scaleLabel) / 2;
if (isHorizontal) {
scaleLabelX = me.left + ((me.right - me.left) / 2); // midpoint of the width
--- /dev/null
+'use strict';
+
+/**
+ * @namespace Chart.helpers.options
+ */
+module.exports = {
+ /**
+ * Converts the given line height `value` in pixels for a specific font `size`.
+ * @param {Number|String} value - The lineHeight to parse (eg. 1.6, '14px', '75%', '1.6em').
+ * @param {Number} size - The font size (in pixels) used to resolve relative `value`.
+ * @returns {Number} The effective line height in pixels (size * 1.2 if value is invalid).
+ * @see https://developer.mozilla.org/en-US/docs/Web/CSS/line-height
+ * @since 2.7.0
+ */
+ toLineHeight: function(value, size) {
+ var matches = (''+value).match(/^(normal|(\d+(?:\.\d+)?)(px|em|%)?)$/);
+ if (!matches || matches[1] === 'normal') {
+ return size * 1.2;
+ }
+
+ value = parseFloat(matches[2]);
+
+ switch (matches[3]) {
+ case 'px':
+ return value;
+ case '%':
+ value /= 100;
+ break;
+ default:
+ break;
+ }
+
+ return size * value;
+ }
+};
module.exports = require('./helpers.core');
module.exports.easing = require('./helpers.easing');
module.exports.canvas = require('./helpers.canvas');
+module.exports.options = require('./helpers.options');
module.exports.time = require('./helpers.time');
display: false,
fontStyle: 'bold',
fullWidth: true,
+ lineHeight: 1.2,
padding: 10,
position: 'top',
text: '',
fontSize = valueOrDefault(opts.fontSize, defaults.global.defaultFontSize),
minSize = me.minSize,
lineCount = helpers.isArray(opts.text) ? opts.text.length : 1,
- lineHeight = valueOrDefault(opts.lineHeight, fontSize),
+ lineHeight = helpers.options.toLineHeight(opts.lineHeight, fontSize),
textSize = display ? (lineCount * lineHeight) + (opts.padding * 2) : 0;
if (me.isHorizontal()) {
fontStyle = valueOrDefault(opts.fontStyle, globalDefaults.defaultFontStyle),
fontFamily = valueOrDefault(opts.fontFamily, globalDefaults.defaultFontFamily),
titleFont = helpers.fontString(fontSize, fontStyle, fontFamily),
- lineHeight = valueOrDefault(opts.lineHeight, fontSize),
+ lineHeight = helpers.options.toLineHeight(opts.lineHeight, fontSize),
+ offset = lineHeight/2 + opts.padding,
rotation = 0,
titleX,
titleY,
// Horizontal
if (me.isHorizontal()) {
titleX = left + ((right - left) / 2); // midpoint of the width
- titleY = top + ((bottom - top) / 2); // midpoint of the height
+ titleY = top + offset;
maxWidth = right - left;
} else {
- titleX = opts.position === 'left' ? left + (fontSize / 2) : right - (fontSize / 2);
+ titleX = opts.position === 'left' ? left + offset : right - offset;
titleY = top + ((bottom - top) / 2);
maxWidth = bottom - top;
rotation = Math.PI * (opts.position === 'left' ? -0.5 : 0.5);
},
position: 'right',
scaleLabel: {
- labelString: '',
display: false,
+ labelString: '',
+ lineHeight: 1.2
},
ticks: {
beginAtZero: false,
},
position: 'left',
scaleLabel: {
- labelString: '',
display: false,
+ labelString: '',
+ lineHeight: 1.2
},
ticks: {
beginAtZero: false,
--- /dev/null
+'use strict';
+
+describe('Chart.helpers.options', function() {
+ var options = Chart.helpers.options;
+
+ describe('toLineHeight', function() {
+ it ('should support keyword values', function() {
+ expect(options.toLineHeight('normal', 16)).toBe(16 * 1.2);
+ });
+ it ('should support unitless values', function() {
+ expect(options.toLineHeight(1.4, 16)).toBe(16 * 1.4);
+ expect(options.toLineHeight('1.4', 16)).toBe(16 * 1.4);
+ });
+ it ('should support length values', function() {
+ expect(options.toLineHeight('42px', 16)).toBe(42);
+ expect(options.toLineHeight('1.4em', 16)).toBe(16 * 1.4);
+ });
+ it ('should support percentage values', function() {
+ expect(options.toLineHeight('140%', 16)).toBe(16 * 1.4);
+ });
+ it ('should fallback to default (1.2) for invalid values', function() {
+ expect(options.toLineHeight(null, 16)).toBe(16 * 1.2);
+ expect(options.toLineHeight(undefined, 16)).toBe(16 * 1.2);
+ expect(options.toLineHeight('foobar', 16)).toBe(16 * 1.2);
+ });
+ });
+});
fullWidth: true,
weight: 2000,
fontStyle: 'bold',
+ lineHeight: 1.2,
padding: 10,
text: ''
});
expect(minSize).toEqual({
width: 400,
- height: 32
+ height: 34.4
});
});
minSize = title.update(200, 400);
expect(minSize).toEqual({
- width: 32,
+ width: 34.4,
height: 400
});
});
options.text = ['line1', 'line2'];
options.position = 'left';
options.display = true;
- options.lineHeight = 15;
+ options.lineHeight = 1.5;
var title = new Chart.Title({
chart: chart,
var minSize = title.update(200, 400);
expect(minSize).toEqual({
- width: 50,
+ width: 56,
height: 400
});
});
args: []
}, {
name: 'translate',
- args: [300, 66]
+ args: [300, 67.2]
}, {
name: 'rotate',
args: [0]
args: []
}, {
name: 'translate',
- args: [106, 250]
+ args: [117.2, 250]
}, {
name: 'rotate',
args: [-0.5 * Math.PI]
args: []
}, {
name: 'translate',
- args: [126, 250]
+ args: [117.2, 250]
}, {
name: 'rotate',
args: [0.5 * Math.PI]
},
position: 'bottom',
scaleLabel: {
+ display: false,
labelString: '',
- display: false
+ lineHeight: 1.2
},
ticks: {
beginAtZero: false,
},
position: 'left',
scaleLabel: {
- labelString: '',
display: false,
+ labelString: '',
+ lineHeight: 1.2
},
ticks: {
beginAtZero: false,
expect(xScale.paddingBottom).toBeCloseToPixel(0);
expect(xScale.paddingLeft).toBeCloseToPixel(0);
expect(xScale.paddingRight).toBeCloseToPixel(0);
- expect(xScale.width).toBeCloseToPixel(450);
- expect(xScale.height).toBeCloseToPixel(46);
+ expect(xScale.width).toBeCloseToPixel(454);
+ expect(xScale.height).toBeCloseToPixel(42);
expect(yScale.paddingTop).toBeCloseToPixel(0);
expect(yScale.paddingBottom).toBeCloseToPixel(0);
expect(yScale.paddingLeft).toBeCloseToPixel(0);
expect(yScale.paddingRight).toBeCloseToPixel(0);
- expect(yScale.width).toBeCloseToPixel(48);
- expect(yScale.height).toBeCloseToPixel(434);
+ expect(yScale.width).toBeCloseToPixel(44);
+ expect(yScale.height).toBeCloseToPixel(438);
});
it('should fit correctly when display is turned off', function() {
drawBorder: false
},
scaleLabel: {
- display: false
+ display: false,
+ lineHeight: 1.2
},
ticks: {
display: false,
},
position: 'left',
scaleLabel: {
- labelString: '',
display: false,
+ labelString: '',
+ lineHeight: 1.2
},
ticks: {
beginAtZero: false,
},
position: 'chartArea',
scaleLabel: {
- labelString: '',
display: false,
+ labelString: '',
+ lineHeight: 1.2
},
ticks: {
backdropColor: 'rgba(255,255,255,0.75)',
},
position: 'bottom',
scaleLabel: {
+ display: false,
labelString: '',
- display: false
+ lineHeight: 1.2
},
ticks: {
beginAtZero: false,