diff --git a/src/ChartInternal/Axis/AxisRendererHelper.ts b/src/ChartInternal/Axis/AxisRendererHelper.ts index b560bca7a..33b698e59 100644 --- a/src/ChartInternal/Axis/AxisRendererHelper.ts +++ b/src/ChartInternal/Axis/AxisRendererHelper.ts @@ -77,9 +77,11 @@ export default class AxisRendererHelper { value => `translate(0,${value})`; return (selection, scale) => { - selection.attr("transform", d => ( - isValue(d) ? fn(Math.ceil(scale(d))) : null - )); + selection.attr("transform", d => { + const x = scale(d); + + return isValue(d) ? fn(Math.ceil(x)) : null; + }); }; } diff --git a/src/ChartInternal/interactions/zoom.ts b/src/ChartInternal/interactions/zoom.ts index b67f62da4..06b5992c7 100644 --- a/src/ChartInternal/interactions/zoom.ts +++ b/src/ChartInternal/interactions/zoom.ts @@ -23,7 +23,9 @@ export default { $$.scale.zoom = null; $$.generateZoom(); - $$.initZoomBehaviour(); + + $$.config.zoom_type === "drag" && + $$.initZoomBehaviour(); }, /** @@ -72,6 +74,7 @@ export default { const ratio = diffDomain($$.scale.x.orgDomain()) / diffDomain($$.getZoomDomain()); const extent = this.orgScaleExtent(); + // https://d3js.org/d3-zoom#zoom_scaleExtent this.scaleExtent([extent[0] * ratio, extent[1] * ratio]); return this; @@ -96,6 +99,11 @@ export default { isRotated ? "rescaleY" : "rescaleX" ](org.xScale || scale.x); + // prevent drag zoom to be out of range + if (newScale.domain().some(v => /(Invalid Date|NaN)/.test(v.toString()))) { + return; + } + const domain = $$.trimXDomain(newScale.domain()); const rescale = config.zoom_rescale; @@ -381,7 +389,7 @@ export default { .attr("height", isRotated ? 0 : state.height); } - start = getPointer(event, this)[prop.index]; + start = getPointer(event, this as SVGAElement)[prop.index]; end = start; zoomRect @@ -391,7 +399,7 @@ export default { $$.onZoomStart(event); }) .on("drag", function(event) { - end = getPointer(event, this)[prop.index]; + end = getPointer(event, this as SVGAElement)[prop.index]; zoomRect .attr(prop.axis, Math.min(start, end)) diff --git a/test/api/zoom-spec.ts b/test/api/zoom-spec.ts index ee672e84c..85b646c2a 100644 --- a/test/api/zoom-spec.ts +++ b/test/api/zoom-spec.ts @@ -632,4 +632,81 @@ describe("API zoom", function() { expect($$.scale.zoom).to.be.null; }); }); + + describe("zoom extent", () => { + beforeAll(() => { + chart = util.generate({ + data: { + json: [ + {"date":"2023-09-30 00:00:00","ek_house":0}, + {"date":"2023-10-14 00:00:00","ek_house":0}, + {"date":"2023-10-21 00:00:00","ek_house":0}, + {"date":"2023-10-28 00:00:00","ek_house":0}, + {"date":"2023-11-04 00:00:00","ek_house":0}, + ], + keys: { + x: "date", + value: ["ek_house"], + }, + }, + axis: { + x: { + type: "timeseries", + tick: { + format: "%Y-%m-%d" + }, + }, + y: { + show: false + } + }, + zoom: { + enabled: true + } + }); + }); + + it("shouldn't throw error for timeseries x axis, when is given out of range.", () => new Promise(done => { + chart.zoom([1697701666380, 1697702008724]); + + setTimeout(() => { + chart.$.circles.each(function() { + expect(this.getAttribute("cx") !== "NaN").to.be.true; + }); + + done(1); + }, 300); + })); + + it("shouldn't throw error for indexed x axis, when is given out of range.", () => new Promise(done => { + chart = util.generate({ + data: { + columns: [ + ["data2", 130, 100, 140, 200, 150, 50, 120, 100, 80, 90] + ], + }, + zoom: { + enabled: true + }, + axis: { + y: { + show: false + } + } + }); + + chart.zoom([ + 4.908784864317814, + 4.908812566017803 + ]); + + setTimeout(() => { + chart.$.circles.each(function() { + expect(this.getAttribute("cx") !== "NaN").to.be.true; + }); + + done(1); + }, 300); + })); + }); }); diff --git a/test/internals/bb-spec.ts b/test/internals/bb-spec.ts index 2976e87e7..d59dacb6a 100644 --- a/test/internals/bb-spec.ts +++ b/test/internals/bb-spec.ts @@ -536,7 +536,7 @@ describe("Interface & initialization", () => { })); it("check lazy rendering on callbacks", () => new Promise(done => { - const el = document.body.querySelector("#chart"); + const el = document.body.querySelector("#chart") as HTMLDivElement; // hide to lazy render el.style.display = "none";