Skip to content

Commit

Permalink
feat(axis): Intent to ship axis.evalTextSize
Browse files Browse the repository at this point in the history
Implement axis text's size evaluation option

Fix #3889

Co-authored-by: netil <[email protected]>
  • Loading branch information
netil and netil authored Oct 7, 2024
1 parent 9c668e6 commit 87048e9
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 7 deletions.
8 changes: 6 additions & 2 deletions src/ChartInternal/Axis/AxisRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ export default class AxisRenderer {
tick: axisShow ? params.config[`${prefix}_tick_show`] : false,
text: axisShow ? params.config[`${prefix}_tick_text_show`] : false
};
const evalTextSize = params.config.axis_evalTextSize;

let $g;

Expand Down Expand Up @@ -136,10 +137,13 @@ export default class AxisRenderer {
tickShow.tick && tickEnter.append("line");
tickShow.text && tickEnter.append("text");

const sizeFor1Char = Helper.getSizeFor1Char(tick);
const tickText = tick.select("text");
const sizeFor1Char = isFunction(evalTextSize) ?
evalTextSize.bind(ctx.params.owner.api)(tickText.node()) :
Helper.getSizeFor1Char(tickText, evalTextSize);
const counts: number[] = [];

let tspan: d3Selection = tick.select("text")
let tspan: d3Selection = tickText
.selectAll("tspan")
.data((d, index) => {
const split = params.tickMultiline ?
Expand Down
10 changes: 6 additions & 4 deletions src/ChartInternal/Axis/AxisRendererHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,17 @@ export default class AxisRendererHelper {

/**
* Compute a character dimension
* @param {d3.selection} node <g class=tick> node
* @param {d3.selection} text SVG text selection
* @param {boolean} memoize memoize the calculated size
* @returns {{w: number, h: number}}
* @private
*/
static getSizeFor1Char(node: d3Selection): {w: number, h: number} {
static getSizeFor1Char(text: d3Selection, memoize = true): {w: number, h: number} {
// default size for one character
const size = {
w: 5.5,
h: 11.5
};
const text = node.select("text");

!text.empty() && text
.text("0")
Expand All @@ -57,7 +57,9 @@ export default class AxisRendererHelper {
}
});

this.getSizeFor1Char = () => size;
if (memoize) {
this.getSizeFor1Char = () => size;
}

return size;
}
Expand Down
30 changes: 30 additions & 0 deletions src/config/Options/axis/axis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,36 @@ import y2 from "./y2";
* y Axis config options
*/
export default {
/**
* Setup the way to evaluate tick text size.
* - **NOTE:**
* - Setting `false` or custom evaluator, highly recommended to memoize evaluated text dimension value to not degrade performance.
* @name axis․evalTextSize
* @memberof Options
* @type {boolean|Function}
* @default true
* @example
* axis: {
* // will evaluate getting text size every time.
* evalTextSize: false.
*
* // set a custom evaluator
* evalTextSize: function(textElement) {
* // set some character to be evaluated
* text.textContent = "0";
*
* // get the size
* const box = text.getBBox();
*
* // clear text
* text.textContent = "";
*
* return { w: 7, h: 12};
* }
* }
*/
axis_evalTextSize: <boolean | ((text: SVGTextElement) => {w: number, h: number})>true,

/**
* Switch x and y axis position.
* @name axis․rotated
Expand Down
2 changes: 1 addition & 1 deletion test/api/axis-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ describe("API axis", function() {
},
onrendered: function() {
main = this.internal.$el.main;
resolve();
resolve(1);
}
});
});
Expand Down
67 changes: 67 additions & 0 deletions test/internals/axis-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
/* eslint-disable */
// @ts-nocheck
import {beforeEach, beforeAll, afterAll, describe, expect, it} from "vitest";
import sinon from "sinon";
import {select as d3Select} from "d3-selection";
import {format as d3Format} from "d3-format";
import {timeMinute as d3TimeMinute} from "d3-time";
Expand Down Expand Up @@ -3655,4 +3656,70 @@ describe("AXIS", function() {
});
});
});

describe("axis.evalTextSize", () => {
beforeAll(() => {
args = {
data: {
columns: [
["data2", 130, 100, 140, 200, 150]
]
},
axis: {
x: {
type: "category",
categories: [
"Some label with a very long text which will definitely be wrapped in an odd way",
"Some label with a very long text which will definitely be wrapped in an odd way",
"Some label with a very long text which will definitely be wrapped in an odd way",
"Some label with a very long text which will definitely be wrapped in an odd way",
"Some label with a very long text which will definitely be wrapped in an odd way"
]
},
evalTextSize: sinon.spy(function(text) {
return {
w: 5,
h: 5
}
})
}
};
});

it("check custom evaluator", () => {
const tick = chart.internal.$el.axis.x.select(".tick").node();
const {width, height} = tick.getBoundingClientRect();

expect(width).to.be.closeTo(116, 3);
expect(height).to.be.closeTo(35, 3);

expect(args.axis.evalTextSize.called).to.be.true;
expect(args.axis.evalTextSize.args[0][0].tagName === "text").to.be.true;
});

it("set options: axis.evalTextSize=false", () => {
args.axis.evalTextSize = false;
});

it("check dimension evaluation not memoized.", () => new Promise(done => {
const text = chart.internal.$el.axis.x.select(".tick text");

// when
text.style("font-size", "5px"); // change font size
chart.resize({width: 300});

setTimeout(() => {
const tick = chart.internal.$el.axis.x.select(".tick").node();
const {width, height} = tick.getBoundingClientRect();

expect(width < 41).to.be.true;
// expect(height < 60).to.be.true;

// reset font-size
text.style("font-size", null);

done(1);
}, 300);
}));
});
});
7 changes: 7 additions & 0 deletions types/axis.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ import {Chart} from "./chart";
* billboard.js project is licensed under the MIT license
*/
export interface Axis {
/**
* Setup the way to evaluate tick text size.
* - **NOTE:**
* - Setting `false` or custom evaluator, highly recommended to memoize evaluated text dimension value to not degrade performance.
*/
evalTextSize?: boolean | ((text: SVGTextElement) => {w: number, h: number});

/**
* Switch x and y axis position.
*/
Expand Down

0 comments on commit 87048e9

Please sign in to comment.