]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
fix: respect minBarLength in stacked bar chart (#10766)
authorKit PANG <pangkit888@gmail.com>
Wed, 23 Nov 2022 01:21:02 +0000 (09:21 +0800)
committerGitHub <noreply@github.com>
Wed, 23 Nov 2022 01:21:02 +0000 (20:21 -0500)
src/controllers/controller.bar.js
src/core/core.datasetController.js
test/fixtures/controller.bar/minBarLength/horizontal-stacked-no-overlap.js [new file with mode: 0644]
test/fixtures/controller.bar/minBarLength/horizontal-stacked-no-overlap.png [new file with mode: 0644]
test/fixtures/controller.bar/minBarLength/vertical-stacked-no-overlap.js [new file with mode: 0644]
test/fixtures/controller.bar/minBarLength/vertical-stacked-no-overlap.png [new file with mode: 0644]
test/specs/core.datasetController.tests.js

index 20e053cc0d5e3d907a1c55e2c0e949370c50d303..1221b64c323c411f56d899bbdc5535027e56a9cd 100644 (file)
@@ -538,7 +538,7 @@ export default class BarController extends DatasetController {
         * @private
         */
   _calculateBarValuePixels(index) {
-    const {_cachedMeta: {vScale, _stacked}, options: {base: baseValue, minBarLength}} = this;
+    const {_cachedMeta: {vScale, _stacked, index: datasetIndex}, options: {base: baseValue, minBarLength}} = this;
     const actualBase = baseValue || 0;
     const parsed = this.getParsed(index);
     const custom = parsed._custom;
@@ -586,6 +586,11 @@ export default class BarController extends DatasetController {
       const max = Math.max(startPixel, endPixel);
       base = Math.max(Math.min(base, max), min);
       head = base + size;
+
+      if (_stacked && !floating) {
+        // visual data coordinates after applying minBarLength
+        parsed._stacks[vScale.axis]._visualValues[datasetIndex] = vScale.getValueForPixel(head) - vScale.getValueForPixel(base);
+      }
     }
 
     if (base === vScale.getPixelForValue(actualBase)) {
index d5b43da8df8e28391321da18dfdf10172d139da2..108460e2aed946b7dac75840734398b35be09d0e 100644 (file)
@@ -158,6 +158,9 @@ function updateStacks(controller, parsed) {
 
     stack._top = getLastIndexInStack(stack, vScale, true, meta.type);
     stack._bottom = getLastIndexInStack(stack, vScale, false, meta.type);
+
+    const visualValues = stack._visualValues || (stack._visualValues = {});
+    visualValues[datasetIndex] = value;
   }
 }
 
@@ -207,6 +210,9 @@ function clearStacks(meta, items) {
       return;
     }
     delete stacks[axis][datasetIndex];
+    if (stacks[axis]._visualValues !== undefined && stacks[axis]._visualValues[datasetIndex] !== undefined) {
+      delete stacks[axis]._visualValues[datasetIndex];
+    }
   }
 }
 
@@ -578,7 +584,7 @@ export default class DatasetController {
     const value = parsed[scale.axis];
     const stack = {
       keys: getSortedDatasetIndices(chart, true),
-      values: parsed._stacks[scale.axis]
+      values: parsed._stacks[scale.axis]._visualValues
     };
     return applyStack(stack, value, meta.index, {mode});
   }
diff --git a/test/fixtures/controller.bar/minBarLength/horizontal-stacked-no-overlap.js b/test/fixtures/controller.bar/minBarLength/horizontal-stacked-no-overlap.js
new file mode 100644 (file)
index 0000000..57b8314
--- /dev/null
@@ -0,0 +1,55 @@
+const minBarLength = 50;
+
+module.exports = {
+  config: {
+    type: 'bar',
+    data: {
+      labels: [1, 2, 3, 4],
+      datasets: [
+        {
+          data: [1, -1, 1, 20],
+          backgroundColor: '#bb000066',
+          minBarLength
+        },
+        {
+          data: [1, -1, -1, -20],
+          backgroundColor: '#00bb0066',
+          minBarLength
+        },
+        {
+          data: [1, -1, 1, 40],
+          backgroundColor: '#0000bb66',
+          minBarLength
+        },
+        {
+          data: [1, -1, -1, -40],
+          backgroundColor: '#00000066',
+          minBarLength
+        }
+      ]
+    },
+    options: {
+      indexAxis: 'y',
+      scales: {
+        x: {
+          display: false,
+          stacked: true
+        },
+        y: {
+          type: 'linear',
+          position: 'left',
+          stacked: true,
+          ticks: {
+            display: false
+          }
+        }
+      }
+    }
+  },
+  options: {
+    canvas: {
+      height: 512,
+      width: 512
+    }
+  }
+};
diff --git a/test/fixtures/controller.bar/minBarLength/horizontal-stacked-no-overlap.png b/test/fixtures/controller.bar/minBarLength/horizontal-stacked-no-overlap.png
new file mode 100644 (file)
index 0000000..dfa3f87
Binary files /dev/null and b/test/fixtures/controller.bar/minBarLength/horizontal-stacked-no-overlap.png differ
diff --git a/test/fixtures/controller.bar/minBarLength/vertical-stacked-no-overlap.js b/test/fixtures/controller.bar/minBarLength/vertical-stacked-no-overlap.js
new file mode 100644 (file)
index 0000000..454e027
--- /dev/null
@@ -0,0 +1,54 @@
+const minBarLength = 50;
+
+module.exports = {
+  config: {
+    type: 'bar',
+    data: {
+      labels: [1, 2, 3, 4],
+      datasets: [
+        {
+          data: [1, -1, 1, 20],
+          backgroundColor: '#bb000066',
+          minBarLength
+        },
+        {
+          data: [1, -1, -1, -20],
+          backgroundColor: '#00bb0066',
+          minBarLength
+        },
+        {
+          data: [1, -1, 1, 40],
+          backgroundColor: '#0000bb66',
+          minBarLength
+        },
+        {
+          data: [1, -1, -1, -40],
+          backgroundColor: '#00000066',
+          minBarLength
+        }
+      ]
+    },
+    options: {
+      scales: {
+        x: {
+          display: false,
+          stacked: true
+        },
+        y: {
+          type: 'linear',
+          position: 'left',
+          stacked: true,
+          ticks: {
+            display: false
+          }
+        }
+      }
+    }
+  },
+  options: {
+    canvas: {
+      height: 512,
+      width: 512
+    }
+  }
+};
diff --git a/test/fixtures/controller.bar/minBarLength/vertical-stacked-no-overlap.png b/test/fixtures/controller.bar/minBarLength/vertical-stacked-no-overlap.png
new file mode 100644 (file)
index 0000000..f6b917a
Binary files /dev/null and b/test/fixtures/controller.bar/minBarLength/vertical-stacked-no-overlap.png differ
index c3835bcafe0f6bf07f88a4c4e2fcb7adcdd3859d..e10c33d118c3e80404bb477e4a8351f34b4f092c 100644 (file)
@@ -768,12 +768,12 @@ describe('Chart.DatasetController', function() {
 
     expect(chart._stacks).toEqual({
       'x.y.1': {
-        0: {0: 1, 2: 3, _top: 2, _bottom: null},
-        1: {0: 10, 2: 30, _top: 2, _bottom: null}
+        0: {0: 1, 2: 3, _top: 2, _bottom: null, _visualValues: {0: 1, 2: 3}},
+        1: {0: 10, 2: 30, _top: 2, _bottom: null, _visualValues: {0: 10, 2: 30}}
       },
       'x.y.2': {
-        0: {1: 2, _top: 1, _bottom: null},
-        1: {1: 20, _top: 1, _bottom: null}
+        0: {1: 2, _top: 1, _bottom: null, _visualValues: {1: 2}},
+        1: {1: 20, _top: 1, _bottom: null, _visualValues: {1: 20}}
       }
     });
 
@@ -782,12 +782,12 @@ describe('Chart.DatasetController', function() {
 
     expect(chart._stacks).toEqual({
       'x.y.1': {
-        0: {0: 1, _top: 2, _bottom: null},
-        1: {0: 10, _top: 2, _bottom: null}
+        0: {0: 1, _top: 2, _bottom: null, _visualValues: {0: 1}},
+        1: {0: 10, _top: 2, _bottom: null, _visualValues: {0: 10}}
       },
       'x.y.2': {
-        0: {1: 2, 2: 3, _top: 2, _bottom: null},
-        1: {1: 20, 2: 30, _top: 2, _bottom: null}
+        0: {1: 2, 2: 3, _top: 2, _bottom: null, _visualValues: {1: 2, 2: 3}},
+        1: {1: 20, 2: 30, _top: 2, _bottom: null, _visualValues: {1: 20, 2: 30}}
       }
     });
   });
@@ -812,12 +812,12 @@ describe('Chart.DatasetController', function() {
 
     expect(chart._stacks).toEqual({
       'x.y.1': {
-        0: {0: 1, 2: 3, _top: 2, _bottom: null},
-        1: {0: 10, 2: 30, _top: 2, _bottom: null}
+        0: {0: 1, 2: 3, _top: 2, _bottom: null, _visualValues: {0: 1, 2: 3}},
+        1: {0: 10, 2: 30, _top: 2, _bottom: null, _visualValues: {0: 10, 2: 30}}
       },
       'x.y.2': {
-        0: {1: 2, _top: 1, _bottom: null},
-        1: {1: 20, _top: 1, _bottom: null}
+        0: {1: 2, _top: 1, _bottom: null, _visualValues: {1: 2}},
+        1: {1: 20, _top: 1, _bottom: null, _visualValues: {1: 20}}
       }
     });
 
@@ -826,12 +826,12 @@ describe('Chart.DatasetController', function() {
 
     expect(chart._stacks).toEqual({
       'x.y.1': {
-        0: {0: 1, 2: 4, _top: 2, _bottom: null},
-        1: {0: 10, _top: 2, _bottom: null}
+        0: {0: 1, 2: 4, _top: 2, _bottom: null, _visualValues: {0: 1, 2: 4}},
+        1: {0: 10, _top: 2, _bottom: null, _visualValues: {0: 10}}
       },
       'x.y.2': {
-        0: {1: 2, _top: 1, _bottom: null},
-        1: {1: 20, _top: 1, _bottom: null}
+        0: {1: 2, _top: 1, _bottom: null, _visualValues: {1: 2}},
+        1: {1: 20, _top: 1, _bottom: null, _visualValues: {1: 20}}
       }
     });
   });
@@ -947,7 +947,7 @@ describe('Chart.DatasetController', function() {
     });
 
     var meta = chart.getDatasetMeta(0);
-    expect(meta._parsed[0]._stacks).toEqual(jasmine.objectContaining({y: {0: 10, 1: 20, _top: 1, _bottom: null}}));
+    expect(meta._parsed[0]._stacks).toEqual(jasmine.objectContaining({y: {0: 10, 1: 20, _top: 1, _bottom: null, _visualValues: {0: 10, 1: 20}}}));
   });
 
   describe('resolveDataElementOptions', function() {