diff --git a/src/ChartInternal/internals/text.ts b/src/ChartInternal/internals/text.ts index ff09ba8f1..147793727 100644 --- a/src/ChartInternal/internals/text.ts +++ b/src/ChartInternal/internals/text.ts @@ -35,18 +35,27 @@ function getRotateAnchor(angle: number): Anchor { /** * Set rotated position coordinate according text.labels.rotate angle - * @param {number} value Data value + * @param {object} d Data object * @param {object} pos Position object * @param {object} pos.x x coordinate * @param {object} pos.y y coordinate * @param {string} anchor string value * @param {boolean} isRotated If axis is rotated + * @param {boolean} isInverted If axis is inverted * @returns {object} x, y coordinate * @private */ -function setRotatePos(value: number, pos: Coord, anchor: Anchor, isRotated: boolean): Coord { +function setRotatePos( + d: IDataRow, pos: Coord, anchor: Anchor, isRotated: boolean, isInverted: boolean +): Coord { + const $$ = this; + const {value} = d; + const isCandlestickType = $$.isCandlestickType(d); + const isNegative = value < 0 || ( + isCandlestickType && !$$.getCandlestickData(d)?._isUp + ); + let {x, y} = pos; - const isNegative = value < 0; const gap = 4; const doubleGap = gap * 2; @@ -71,6 +80,10 @@ function setRotatePos(value: number, pos: Coord, anchor: Anchor, isRotated: bool x -= gap; isNegative && (y += doubleGap * 2); } + + if (isInverted) { + y += isNegative ? -17 : (isCandlestickType ? 13 : 7); + } } return {x, y}; @@ -244,7 +257,7 @@ export default { */ redrawText(getX, getY, forFlow?: boolean, withTransition?: boolean): true { const $$ = this; - const {$T, config} = $$; + const {$T, axis, config} = $$; const t = getRandom(true); const isRotated = config.axis_rotated; const angle = config.data_labels.rotate; @@ -259,13 +272,14 @@ export default { .each(function(d: IDataRow, i: number) { // do not apply transition for newly added text elements const node = $T(this, !!(withTransition && this.getAttribute("x")), t); + const isInverted = config[`axis_${axis?.getId(d.id)}_inverted`]; let pos = { x: getX.bind(this)(d, i), y: getY.bind(this)(d, i) }; if (angle) { - pos = setRotatePos(d.value, pos, anchorString, isRotated); + pos = setRotatePos.bind($$)(d, pos, anchorString, isRotated, isInverted); node.attr("text-anchor", anchorString); } @@ -413,7 +427,6 @@ export default { const isRotated = config.axis_rotated; let xPos = points[0][0]; - if ($$.isCandlestickType(d)) { if (isRotated) { xPos = $$.getCandlestickData(d)?._isUp ? @@ -459,8 +472,10 @@ export default { */ getYForText(points, d, textElement): number { const $$ = this; - const {config, state} = $$; + const {axis, config, state} = $$; const isRotated = config.axis_rotated; + const isInverted = config[`axis_${axis?.getId(d.id)}_inverted`]; + const isBarType = $$.isBarType(d); const r = config.point_r; const rect = getBoundingRect(textElement); let {value} = d; @@ -477,6 +492,10 @@ export default { yPos = value && value._isUp ? points[2][2] - baseY : points[2][1] + (baseY * 4); + + if (isInverted) { + yPos += 15 * (value._isUp ? 1 : -1); + } } } else { if (isRotated) { @@ -489,16 +508,22 @@ export default { } if (value < 0 || (value === 0 && !state.hasPositiveValue && state.hasNegativeValue)) { - yPos += rect.height + ($$.isBarType(d) ? -baseY : baseY); + yPos += isInverted ? (isBarType ? -3 : -5) : ( + rect.height + (isBarType ? -baseY : baseY) + ); } else { let diff = -baseY * 2; - if ($$.isBarType(d)) { + if (isBarType) { diff = -baseY; } else if ($$.isBubbleType(d)) { diff = baseY; } + if (isInverted) { + diff = isBarType ? 10 : 15; + } + yPos += diff; } } diff --git a/test/internals/text-spec.ts b/test/internals/text-spec.ts index 3ede4c8c5..1d35695f0 100644 --- a/test/internals/text-spec.ts +++ b/test/internals/text-spec.ts @@ -1315,6 +1315,92 @@ describe("TEXT", () => { }); }); + describe("Labels' postion on inverted axis", () => { + before(() => { + args = { + data: { + columns: [ + ["data1", + [1027, 1369, 1289, 1348], + [1348, 1371, 1314, 1320], + ] + + ], + type: "candlestick", + labels: { + rotate: 0 + }, + }, + axis: { + y: { + inverted: true + } + } + }; + }); + + it("check for candlestick type", () => { + const expectedY = [390, 321]; + + chart.$.text.texts.each(function(d, i) { + expect(+this.getAttribute("y")).to.be.closeTo(expectedY[i], 2); + }); + }); + + it("set options", () => { + args.data.columns = [["data1", 90, -100]]; + args.data.type = "line"; + }); + + it("check for line type", () => { + const expectedY = [394, 42]; + + chart.$.text.texts.each(function(d, i) { + expect(+this.getAttribute("y")).to.be.closeTo(expectedY[i], 2); + }); + }); + + it("set options: data.type='bar'", () => { + args.data.type = "bar"; + }); + + it("check for bar type", () => { + const expectedY = [389, 44]; + + chart.$.text.texts.each(function(d, i) { + expect(+this.getAttribute("y")).to.be.closeTo(expectedY[i], 2); + }); + }); + + it("set options: data.labels.rotate = 270", () => { + args.data.labels.rotate = 270; + }); + + it("check for bar type with rotate option", () => { + const expectedY = [396, 43]; + + chart.$.text.texts.each(function(d, i) { + const y = +this.getAttribute("transform").match(/\s(\d+\.\d+)/)[1]; + + expect(y).to.be.closeTo(expectedY[i], 2); + }); + }); + + it("set options: data.type = 'line'", () => { + args.data.type = "line"; + }); + + it("check for line type with rotate option", () => { + const expectedY = [401, 41]; + + chart.$.text.texts.each(function(d, i) { + const y = +this.getAttribute("transform").match(/\s(\d+\.\d+)/)[1]; + + expect(y).to.be.closeTo(expectedY[i], 2); + }); + }); + }); + describe("labels.colors callback", () => { let ctx = [];