Repository: chartjs/chartjs-plugin-zoom
Pull request #903 (2024-11-23 18:43:45)

Add devcontainer
This adds a simple Devcontainer which eases contribution without needing to install all dependencies locally. 
This can be used e.g. in VSCode to have a reproducible build environment. 
https://code.visualstudio.com/docs/devcontainers/containers


Patch:
diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile
new file mode 100644
index 0000000..ec01434
--- /dev/null
+++ b/.devcontainer/Dockerfile
@@ -0,0 +1,7 @@
+FROM node:23-alpine3.19
+RUN apk add --no-cache \
+                git \
+                nano\
+                openssh
+
+USER node
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
new file mode 100644
index 0000000..33cb276
--- /dev/null
+++ b/.devcontainer/devcontainer.json
@@ -0,0 +1,22 @@
+{
+  "name": "chartjs-plugin-zoom devcontainer",
+  "dockerFile": "Dockerfile",
+    // Use 'postCreateCommand' to run commands after the container is created.
+    "postCreateCommand": "npm install",
+    "customizations": {
+      // Configure properties specific to VS Code.
+        "vscode": {
+          "settings": {},
+          "extensions": [
+            "eamodio.gitlens",
+            "EditorConfig.EditorConfig"
+          ]
+        }
+    },
+  "containerEnv": {
+    "GIT_EDITOR": "nano"
+  },
+  "mounts": [
+    "type=bind,source=/home/${localEnv:USER}/.ssh,target=/home/node/.ssh,readonly"
+  ]
+}
diff --git a/src/hammer.js b/src/hammer.js
index d8cb94c..97051ae 100644
--- a/src/hammer.js
+++ b/src/hammer.js
@@ -69,8 +69,12 @@ function handlePinch(chart, state, e) {
 function startPinch(chart, state, event) {
   if (state.options.zoom.pinch.enabled) {
     const point = getRelativePosition(event, chart);
-    call(state.options.zoom.onZoomStart, [{chart, event, point}]);
-    state.scale = 1;
+    if (call(state.options.zoom.onZoomStart, [{chart, event, point}]) === false) {
+      state.scale = null;
+      call(state.options.zoom.onZoomRejected, [{chart, event}]);
+    } else {
+      state.scale = 1;
+    }
   }
 }
 


Test patch:
diff --git a/test/specs/zoom.pinch.spec.js b/test/specs/zoom.pinch.spec.js
new file mode 100644
index 0000000..fab012a
--- /dev/null
+++ b/test/specs/zoom.pinch.spec.js
@@ -0,0 +1,98 @@
+describe('pinch', () => {
+  const data = {
+    datasets: [{
+      data: [{
+        x: 1,
+        y: 3
+      }, {
+        x: 2,
+        y: 2
+      }, {
+        x: 3,
+        y: 1
+      }]
+    }]
+  };
+
+  describe('events', () => {
+    it('should call onZoomStart', function(done) {
+      const startSpy = jasmine.createSpy('started');
+      const chart = window.acquireChart({
+        type: 'scatter',
+        data,
+        options: {
+          plugins: {
+            zoom: {
+              zoom: {
+                mode: 'xy',
+                onZoomStart: startSpy,
+                pinch: {
+                  enabled: true,
+                },
+              },
+            }
+          }
+        }
+      });
+
+      Simulator.gestures.pinch(chart.canvas, {pos: [chart.width / 2, chart.height / 2]}, function() {
+        expect(startSpy).toHaveBeenCalled();
+        expect(chart.scales.x.min).not.toBe(1);
+        done();
+      });
+    });
+
+    it('should call onZoomRejected when onStartZoom returns false', function(done) {
+      const rejectSpy = jasmine.createSpy('rejected');
+      const chart = window.acquireChart({
+        type: 'scatter',
+        data,
+        options: {
+          plugins: {
+            zoom: {
+              zoom: {
+                mode: 'xy',
+                onZoomStart: () => false,
+                onZoomRejected: rejectSpy,
+                pinch: {
+                  enabled: true,
+                }
+              }
+            }
+          }
+        }
+      });
+
+      Simulator.gestures.pinch(chart.canvas, {}, function() {
+        expect(rejectSpy).toHaveBeenCalled();
+        expect(chart.scales.x.min).toBe(1);
+        done();
+      });
+    });
+
+    it('should call onZoomComplete', function(done) {
+      const chart = window.acquireChart({
+        type: 'scatter',
+        data,
+        options: {
+          plugins: {
+            zoom: {
+              zoom: {
+                mode: 'xy',
+                onZoomComplete(ctx) {
+                  expect(ctx.chart.scales.x.min).not.toBe(1);
+                  done();
+                },
+                pinch: {
+                  enabled: true,
+                },
+              },
+            }
+          }
+        }
+      });
+      Simulator.gestures.pinch(chart.canvas, {});
+    });
+
+  });
+});