]> git.ipfire.org Git - thirdparty/Chart.js.git/commitdiff
Only enable the bar borderRadius at the end of the stacks (#8941)
authorEvert Timberg <evert.timberg+github@gmail.com>
Sun, 18 Apr 2021 20:30:54 +0000 (16:30 -0400)
committerGitHub <noreply@github.com>
Sun, 18 Apr 2021 20:30:54 +0000 (16:30 -0400)
* Only enable the bar borderRadius at the end of the stacks
* Float bars always get borders enabled
* Tests
* Update documentation

13 files changed:
docs/charts/bar.md
src/controllers/controller.bar.js
src/core/core.datasetController.js
src/elements/element.bar.js
test/fixtures/controller.bar/borderRadius/border-radius-stacked-number-mixed-chart.js [new file with mode: 0644]
test/fixtures/controller.bar/borderRadius/border-radius-stacked-number-mixed-chart.png [new file with mode: 0644]
test/fixtures/controller.bar/borderRadius/border-radius-stacked-number-with-order.js [new file with mode: 0644]
test/fixtures/controller.bar/borderRadius/border-radius-stacked-number-with-order.png [new file with mode: 0644]
test/fixtures/controller.bar/borderRadius/border-radius-stacked-number.js [new file with mode: 0644]
test/fixtures/controller.bar/borderRadius/border-radius-stacked-number.png [new file with mode: 0644]
test/fixtures/controller.bar/borderRadius/border-radius.js [moved from test/fixtures/controller.bar/border-radius.js with 100% similarity]
test/fixtures/controller.bar/borderRadius/border-radius.png [moved from test/fixtures/controller.bar/border-radius.png with 100% similarity]
test/specs/core.datasetController.tests.js

index d4d0a271fdf85a24d46c585532560e21d40d7171..71473b41dd0369fd335eff1f696574e211d3d9e8 100644 (file)
@@ -171,6 +171,10 @@ If this value is a number, it is applied to all sides of the rectangle (left, to
 
 If this value is a number, it is applied to all corners of the rectangle (topLeft, topRight, bottomLeft, bottomRight), except corners touching the [`borderSkipped`](#borderskipped). If this value is an object, the `topLeft` property defines the top-left corners border radius. Similarly, the `topRight`, `bottomLeft`, and `bottomRight` properties can also be specified. Omitted corners and those touching the [`borderSkipped`](#borderskipped) are skipped. For example if the `top` border is skipped, the border radius for the corners `topLeft` and `topRight` will be skipped as well.
 
+:::tip Stacked Charts
+When the border radius is supplied as a number and the chart is stacked, the radius will only be applied to the bars that are at the edges of the stack or where the bar is floating. The object syntax can be used to override this behavior.
+:::
+
 ### Interactions
 
 The interaction with each bar can be controlled with the following properties:
index abe98841c5874e375bfff2fad3906aa2fb423e4a..0ed99b7ec8bee9eb72ff1c7e54b1e267f7cf0ecc 100644 (file)
@@ -269,10 +269,12 @@ export default class BarController extends DatasetController {
       const parsed = me.getParsed(i);
       const vpixels = reset || isNullOrUndef(parsed[vScale.axis]) ? {base, head: base} : me._calculateBarValuePixels(i);
       const ipixels = me._calculateBarIndexPixels(i, ruler);
+      const stack = (parsed._stacks || {})[vScale.axis];
 
       const properties = {
         horizontal,
         base: vpixels.base,
+        enableBorderRadius: !stack || isFloatBar(parsed._custom) || (me.index === stack._top || me.index === stack._bottom),
         x: horizontal ? vpixels.head : ipixels.center,
         y: horizontal ? ipixels.center : vpixels.head,
         height: horizontal ? ipixels.size : undefined,
index a3882e9e66da89d2e7ce13d8762c4a976c0e5023..cff882ab865c526253ccccffe0222f953d4b9ffd 100644 (file)
@@ -127,6 +127,17 @@ function getOrCreateStack(stacks, stackKey, indexValue) {
   return subStack[indexValue] || (subStack[indexValue] = {});
 }
 
+function getLastIndexInStack(stack, vScale, positive) {
+  for (const meta of vScale.getMatchingVisibleMetas('bar').reverse()) {
+    const value = stack[meta.index];
+    if ((positive && value > 0) || (!positive && value < 0)) {
+      return meta.index;
+    }
+  }
+
+  return null;
+}
+
 function updateStacks(controller, parsed) {
   const {chart, _cachedMeta: meta} = controller;
   const stacks = chart._stacks || (chart._stacks = {}); // map structure is {stackKey: {datasetIndex: value}}
@@ -143,6 +154,9 @@ function updateStacks(controller, parsed) {
     const itemStacks = item._stacks || (item._stacks = {});
     stack = itemStacks[vAxis] = getOrCreateStack(stacks, key, index);
     stack[datasetIndex] = value;
+
+    stack._top = getLastIndexInStack(stack, vScale, true);
+    stack._bottom = getLastIndexInStack(stack, vScale, false);
   }
 }
 
index 8d90068043577ae5b14c0faa5d490c16b4b2dfeb..c7e82309ec640e4cffef020298ed604f6f3d6779 100644 (file)
@@ -1,4 +1,5 @@
 import Element from '../core/core.element';
+import {isObject} from '../helpers';
 import {addRoundedRectPath} from '../helpers/helpers.canvas';
 import {toTRBL, toTRBLCorners} from '../helpers/helpers.options';
 
@@ -83,16 +84,21 @@ function parseBorderWidth(bar, maxW, maxH) {
 }
 
 function parseBorderRadius(bar, maxW, maxH) {
+  const {enableBorderRadius} = bar.getProps(['enableBorderRadius']);
   const value = bar.options.borderRadius;
   const o = toTRBLCorners(value);
   const maxR = Math.min(maxW, maxH);
   const skip = parseBorderSkipped(bar);
 
+  // If the value is an object, assume the user knows what they are doing
+  // and apply as directed.
+  const enableBorder = enableBorderRadius || isObject(value);
+
   return {
-    topLeft: skipOrLimit(skip.top || skip.left, o.topLeft, 0, maxR),
-    topRight: skipOrLimit(skip.top || skip.right, o.topRight, 0, maxR),
-    bottomLeft: skipOrLimit(skip.bottom || skip.left, o.bottomLeft, 0, maxR),
-    bottomRight: skipOrLimit(skip.bottom || skip.right, o.bottomRight, 0, maxR)
+    topLeft: skipOrLimit(!enableBorder || skip.top || skip.left, o.topLeft, 0, maxR),
+    topRight: skipOrLimit(!enableBorder || skip.top || skip.right, o.topRight, 0, maxR),
+    bottomLeft: skipOrLimit(!enableBorder || skip.bottom || skip.left, o.bottomLeft, 0, maxR),
+    bottomRight: skipOrLimit(!enableBorder || skip.bottom || skip.right, o.bottomRight, 0, maxR)
   };
 }
 
@@ -224,6 +230,7 @@ BarElement.defaults = {
   borderSkipped: 'start',
   borderWidth: 0,
   borderRadius: 0,
+  enableBorderRadius: true,
   pointStyle: undefined
 };
 
diff --git a/test/fixtures/controller.bar/borderRadius/border-radius-stacked-number-mixed-chart.js b/test/fixtures/controller.bar/borderRadius/border-radius-stacked-number-mixed-chart.js
new file mode 100644 (file)
index 0000000..8970379
--- /dev/null
@@ -0,0 +1,42 @@
+module.exports = {
+  threshold: 0.01,
+  config: {
+    type: 'bar',
+    data: {
+      labels: [0, 1, 2, 3, 4, 5],
+      datasets: [
+        {
+          backgroundColor: 'red',
+          data: [12, 19, 12, 5, 4, 12],
+        },
+        {
+          backgroundColor: 'green',
+          data: [12, 19, -4, 5, 8, 3],
+          type: 'line'
+        },
+        {
+          backgroundColor: 'blue',
+          data: [7, 11, -12, 12, 0, -7],
+        }
+      ]
+    },
+    options: {
+      elements: {
+        bar: {
+          borderRadius: Number.MAX_VALUE,
+          borderWidth: 2,
+        }
+      },
+      scales: {
+        x: {display: false, stacked: true},
+        y: {display: false, stacked: true}
+      }
+    }
+  },
+  options: {
+    canvas: {
+      height: 256,
+      width: 512
+    }
+  }
+};
diff --git a/test/fixtures/controller.bar/borderRadius/border-radius-stacked-number-mixed-chart.png b/test/fixtures/controller.bar/borderRadius/border-radius-stacked-number-mixed-chart.png
new file mode 100644 (file)
index 0000000..3aff738
Binary files /dev/null and b/test/fixtures/controller.bar/borderRadius/border-radius-stacked-number-mixed-chart.png differ
diff --git a/test/fixtures/controller.bar/borderRadius/border-radius-stacked-number-with-order.js b/test/fixtures/controller.bar/borderRadius/border-radius-stacked-number-with-order.js
new file mode 100644 (file)
index 0000000..886e9c4
--- /dev/null
@@ -0,0 +1,44 @@
+module.exports = {
+  threshold: 0.01,
+  config: {
+    type: 'bar',
+    data: {
+      labels: [0, 1, 2, 3, 4, 5],
+      datasets: [
+        {
+          backgroundColor: 'red',
+          data: [12, 19, 12, 5, 4, 12],
+          order: 2,
+        },
+        {
+          backgroundColor: 'green',
+          data: [12, 19, -4, 5, 8, 3],
+          order: 1,
+        },
+        {
+          backgroundColor: 'blue',
+          data: [7, 11, -12, 12, 0, -7],
+          order: 0,
+        }
+      ]
+    },
+    options: {
+      elements: {
+        bar: {
+          borderRadius: Number.MAX_VALUE,
+          borderWidth: 2,
+        }
+      },
+      scales: {
+        x: {display: false, stacked: true},
+        y: {display: false, stacked: true}
+      }
+    }
+  },
+  options: {
+    canvas: {
+      height: 256,
+      width: 512
+    }
+  }
+};
diff --git a/test/fixtures/controller.bar/borderRadius/border-radius-stacked-number-with-order.png b/test/fixtures/controller.bar/borderRadius/border-radius-stacked-number-with-order.png
new file mode 100644 (file)
index 0000000..24eb8e0
Binary files /dev/null and b/test/fixtures/controller.bar/borderRadius/border-radius-stacked-number-with-order.png differ
diff --git a/test/fixtures/controller.bar/borderRadius/border-radius-stacked-number.js b/test/fixtures/controller.bar/borderRadius/border-radius-stacked-number.js
new file mode 100644 (file)
index 0000000..1aeac88
--- /dev/null
@@ -0,0 +1,41 @@
+module.exports = {
+  threshold: 0.01,
+  config: {
+    type: 'bar',
+    data: {
+      labels: [0, 1, 2, 3, 4, 5],
+      datasets: [
+        {
+          backgroundColor: 'red',
+          data: [12, 19, 12, 5, 4, 12],
+        },
+        {
+          backgroundColor: 'green',
+          data: [12, 19, -4, 5, 8, 3],
+        },
+        {
+          backgroundColor: 'blue',
+          data: [7, 11, -12, 12, 0, -7],
+        }
+      ]
+    },
+    options: {
+      elements: {
+        bar: {
+          borderRadius: Number.MAX_VALUE,
+          borderWidth: 2,
+        }
+      },
+      scales: {
+        x: {display: false, stacked: true},
+        y: {display: false, stacked: true}
+      }
+    }
+  },
+  options: {
+    canvas: {
+      height: 256,
+      width: 512
+    }
+  }
+};
diff --git a/test/fixtures/controller.bar/borderRadius/border-radius-stacked-number.png b/test/fixtures/controller.bar/borderRadius/border-radius-stacked-number.png
new file mode 100644 (file)
index 0000000..2b8af4b
Binary files /dev/null and b/test/fixtures/controller.bar/borderRadius/border-radius-stacked-number.png differ
index ce7d121b6c9b6d71e67f2e16c274409cf7bc94a0..e89aa0bf98c540b9b7d099b61f18ea69ba440dce 100644 (file)
@@ -540,12 +540,12 @@ describe('Chart.DatasetController', function() {
 
     expect(chart._stacks).toEqual({
       'x.y.1': {
-        0: {0: 1, 2: 3},
-        1: {0: 10, 2: 30}
+        0: {0: 1, 2: 3, _top: 2, _bottom: null},
+        1: {0: 10, 2: 30, _top: 2, _bottom: null}
       },
       'x.y.2': {
-        0: {1: 2},
-        1: {1: 20}
+        0: {1: 2, _top: 1, _bottom: null},
+        1: {1: 20, _top: 1, _bottom: null}
       }
     });
 
@@ -554,12 +554,12 @@ describe('Chart.DatasetController', function() {
 
     expect(chart._stacks).toEqual({
       'x.y.1': {
-        0: {0: 1},
-        1: {0: 10}
+        0: {0: 1, _top: 2, _bottom: null},
+        1: {0: 10, _top: 2, _bottom: null}
       },
       'x.y.2': {
-        0: {1: 2, 2: 3},
-        1: {1: 20, 2: 30}
+        0: {1: 2, 2: 3, _top: 2, _bottom: null},
+        1: {1: 20, 2: 30, _top: 2, _bottom: null}
       }
     });
   });
@@ -584,12 +584,12 @@ describe('Chart.DatasetController', function() {
 
     expect(chart._stacks).toEqual({
       'x.y.1': {
-        0: {0: 1, 2: 3},
-        1: {0: 10, 2: 30}
+        0: {0: 1, 2: 3, _top: 2, _bottom: null},
+        1: {0: 10, 2: 30, _top: 2, _bottom: null}
       },
       'x.y.2': {
-        0: {1: 2},
-        1: {1: 20}
+        0: {1: 2, _top: 1, _bottom: null},
+        1: {1: 20, _top: 1, _bottom: null}
       }
     });
 
@@ -598,12 +598,12 @@ describe('Chart.DatasetController', function() {
 
     expect(chart._stacks).toEqual({
       'x.y.1': {
-        0: {0: 1, 2: 4},
-        1: {0: 10}
+        0: {0: 1, 2: 4, _top: 2, _bottom: null},
+        1: {0: 10, _top: 2, _bottom: null}
       },
       'x.y.2': {
-        0: {1: 2},
-        1: {1: 20}
+        0: {1: 2, _top: 1, _bottom: null},
+        1: {1: 20, _top: 1, _bottom: null}
       }
     });
   });
@@ -719,7 +719,7 @@ describe('Chart.DatasetController', function() {
     });
 
     var meta = chart.getDatasetMeta(0);
-    expect(meta._parsed[0]._stacks).toEqual(jasmine.objectContaining({y: {0: 10, 1: 20}}));
+    expect(meta._parsed[0]._stacks).toEqual(jasmine.objectContaining({y: {0: 10, 1: 20, _top: null, _bottom: null}}));
   });
 
   describe('resolveDataElementOptions', function() {