Repository: chartjs/chartjs-plugin-zoom
Pull request #553 (2021-06-29 09:19:53)

Fix resetZoom after manual scale limits update
Fix: #540

Changes:
- track scale limit updates done by this plugin
- update original scale limits if the limits are not set by this plugin


Patch:
diff --git a/src/core.js b/src/core.js
index 4f978b2..4bda8a8 100644
--- a/src/core.js
+++ b/src/core.js
@@ -3,21 +3,38 @@ import {panFunctions, updateRange, zoomFunctions} from './scale.types';
 import {getState} from './state';
 import {directionEnabled, getEnabledScalesByPoint} from './utils';
 
+function shouldUpdateScaleLimits(scale, originalScaleLimits, updatedScaleLimits) {
+  const {id, options: {min, max}} = scale;
+  if (!originalScaleLimits[id] || !updatedScaleLimits[id]) {
+    return true;
+  }
+  const previous = updatedScaleLimits[id];
+  return previous.min !== min || previous.max !== max;
+}
+
+function removeMissingScales(limits, scales) {
+  each(limits, (opt, key) => {
+    if (!scales[key]) {
+      delete limits[key];
+    }
+  });
+}
+
 function storeOriginalScaleLimits(chart, state) {
-  const {originalScaleLimits} = state;
-  each(chart.scales, function(scale) {
-    if (!originalScaleLimits[scale.id]) {
+  const {scales} = chart;
+  const {originalScaleLimits, updatedScaleLimits} = state;
+
+  each(scales, function(scale) {
+    if (shouldUpdateScaleLimits(scale, originalScaleLimits, updatedScaleLimits)) {
       originalScaleLimits[scale.id] = {
         min: {scale: scale.min, options: scale.options.min},
         max: {scale: scale.max, options: scale.options.max},
       };
     }
   });
-  each(originalScaleLimits, function(opt, key) {
-    if (!chart.scales[key]) {
-      delete originalScaleLimits[key];
-    }
-  });
+
+  removeMissingScales(originalScaleLimits, scales);
+  removeMissingScales(updatedScaleLimits, scales);
   return originalScaleLimits;
 }
 
diff --git a/src/scale.types.js b/src/scale.types.js
index 42d1623..936ff62 100644
--- a/src/scale.types.js
+++ b/src/scale.types.js
@@ -54,6 +54,9 @@ export function updateRange(scale, {min, max}, limits, zoom = false) {
   }
   scaleOpts.min = min;
   scaleOpts.max = max;
+
+  state.updatedScaleLimits[scale.id] = {min, max};
+
   // return true if the scale range is changed
   return scale.parse(min) !== scale.min || scale.parse(max) !== scale.max;
 }
diff --git a/src/state.js b/src/state.js
index 23ba976..f0425cd 100644
--- a/src/state.js
+++ b/src/state.js
@@ -5,6 +5,7 @@ export function getState(chart) {
   if (!state) {
     state = {
       originalScaleLimits: {},
+      updatedScaleLimits: {},
       handlers: {},
       panDelta: {}
     };


Test patch:
diff --git a/test/fixtures/zoom/update-reset.js b/test/fixtures/zoom/update-reset.js
new file mode 100644
index 0000000..774a6f1
--- /dev/null
+++ b/test/fixtures/zoom/update-reset.js
@@ -0,0 +1,89 @@
+const labels = ['a', 'b', 'c', 'd'];
+const data = [2, 4, 8, 16];
+
+const canvas = document.createElement('canvas');
+const side = 256;
+canvas.height = 768;
+canvas.width = 768;
+const ctx = canvas.getContext('2d');
+
+let x = 0;
+let y = 0;
+function snapshot(chart, descr) {
+  ctx.drawImage(chart.canvas, x, y, side, side);
+  ctx.textBaseline = 'middle';
+  ctx.textAlign = 'center';
+  ctx.font = 'normal normal 12px arial';
+  ctx.fillText(descr, x + side / 2, y + side / 2);
+
+  x += side;
+  if (x + side > canvas.width) {
+    y += side;
+    x = 0;
+  }
+}
+
+module.exports = {
+  tolerance: 0.0018,
+  config: {
+    type: 'line',
+    data: {
+      labels,
+      datasets: [{
+        data,
+        borderColor: 'red',
+        borderWidth: 10
+      }]
+    },
+    options: {
+      scales: {
+        y: {}
+      },
+      plugins: {
+        legend: false,
+      },
+    }
+  },
+  options: {
+    spriteText: true,
+    run(chart) {
+      snapshot(chart, 'original');
+
+      chart.zoomScale('y', {min: 3, max: 9});
+      snapshot(chart, 'zoom 3..9');
+
+      chart.resetZoom();
+      snapshot(chart, 'reset');
+
+      chart.options.scales.y = {
+        min: 0,
+        max: 25
+      };
+      chart.update();
+      snapshot(chart, 'update 0..25');
+
+      chart.resetZoom();
+      snapshot(chart, 'reset');
+
+      chart.zoomScale('y', {min: 5, max: 15});
+      snapshot(chart, 'zoom 5..15');
+
+      chart.resetZoom();
+      snapshot(chart, 'reset');
+
+      chart.options.scales.y = {
+        min: 1,
+        max: 10
+      };
+      chart.update();
+      snapshot(chart, 'update 1..10');
+
+      chart.resetZoom();
+      snapshot(chart, 'reset');
+
+      Chart.helpers.clearCanvas(chart.canvas);
+      chart.ctx.drawImage(canvas, 0, 0, 512, 512);
+    }
+  }
+};
+