Skip to content

Commit

Permalink
Only enable the bar borderRadius at the end of the stacks (#8941)
Browse files Browse the repository at this point in the history
* Only enable the bar borderRadius at the end of the stacks
* Float bars always get borders enabled
* Tests
* Update documentation
  • Loading branch information
etimberg committed Apr 18, 2021
1 parent 019cb9f commit b92f9e5
Show file tree
Hide file tree
Showing 13 changed files with 175 additions and 21 deletions.
4 changes: 4 additions & 0 deletions docs/charts/bar.md
Expand Up @@ -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:
Expand Down
2 changes: 2 additions & 0 deletions src/controllers/controller.bar.js
Expand Up @@ -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,
Expand Down
14 changes: 14 additions & 0 deletions src/core/core.datasetController.js
Expand Up @@ -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}}
Expand All @@ -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);
}
}

Expand Down
15 changes: 11 additions & 4 deletions src/elements/element.bar.js
@@ -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';

Expand Down Expand Up @@ -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)
};
}

Expand Down Expand Up @@ -224,6 +230,7 @@ BarElement.defaults = {
borderSkipped: 'start',
borderWidth: 0,
borderRadius: 0,
enableBorderRadius: true,
pointStyle: undefined
};

Expand Down
@@ -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
}
}
};
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@@ -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
}
}
};
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
@@ -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
}
}
};
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
34 changes: 17 additions & 17 deletions test/specs/core.datasetController.tests.js
Expand Up @@ -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}
}
});

Expand All @@ -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}
}
});
});
Expand All @@ -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}
}
});

Expand All @@ -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}
}
});
});
Expand Down Expand Up @@ -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() {
Expand Down

0 comments on commit b92f9e5

Please sign in to comment.