From cc157093c09b4cc602f251a6e504fab3bd309a63 Mon Sep 17 00:00:00 2001 From: Dan Manastireanu <498419+danmana@users.noreply.github.com> Date: Tue, 20 Oct 2020 12:22:16 +0300 Subject: [PATCH 1/8] Add helper to parse border radius options --- src/helpers/helpers.options.js | 27 ++++++++++++++++++++ test/specs/helpers.options.tests.js | 39 ++++++++++++++++++++++++++++- 2 files changed, 65 insertions(+), 1 deletion(-) diff --git a/src/helpers/helpers.options.js b/src/helpers/helpers.options.js index ba61f5e5249..980ca15634c 100644 --- a/src/helpers/helpers.options.js +++ b/src/helpers/helpers.options.js @@ -64,6 +64,33 @@ export function toTRBL(value) { }; } +/** + * Converts the given value into a TRBL corners object (similar with css border-radius). + * @param {number|object} value - If a number, set the value to all TRBL corner components, + * else, if an object, use defined properties and sets undefined ones to 0. + * @returns {object} The TRBL corner values (topLeft, topRight, bottomLeft, bottomRight) + * @since 3.0.0 + */ +export function toTRBLCorners(value) { + let tl, tr, bl, br; + + if (isObject(value)) { + tl = numberOrZero(value.topLeft); + tr = numberOrZero(value.topRight); + bl = numberOrZero(value.bottomLeft); + br = numberOrZero(value.bottomRight); + } else { + tl = tr = bl = br = numberOrZero(value); + } + + return { + topLeft: tl, + topRight: tr, + bottomLeft: bl, + bottomRight: br + }; +} + /** * Converts the given value into a padding object with pre-computed width/height. * @param {number|object} value - If a number, set the value to all TRBL component, diff --git a/test/specs/helpers.options.tests.js b/test/specs/helpers.options.tests.js index f742b1bf548..6bad385c122 100644 --- a/test/specs/helpers.options.tests.js +++ b/test/specs/helpers.options.tests.js @@ -1,4 +1,4 @@ -const {toLineHeight, toPadding, toFont, resolve} = Chart.helpers; // from '../../src/helpers/helpers.options'; +const {toLineHeight, toPadding, toFont, resolve, toTRBLCorners} = Chart.helpers; // from '../../src/helpers/helpers.options'; describe('Chart.helpers.options', function() { describe('toLineHeight', function() { @@ -23,6 +23,43 @@ describe('Chart.helpers.options', function() { }); }); + describe('toTRBLCorners', function() { + it('should support number values', function() { + expect(toTRBLCorners(4)).toEqual( + {topLeft: 4, topRight: 4, bottomLeft: 4, bottomRight: 4}); + expect(toTRBLCorners(4.5)).toEqual( + {topLeft: 4.5, topRight: 4.5, bottomLeft: 4.5, bottomRight: 4.5}); + }); + it('should support string values', function() { + expect(toTRBLCorners('4')).toEqual( + {topLeft: 4, topRight: 4, bottomLeft: 4, bottomRight: 4}); + expect(toTRBLCorners('4.5')).toEqual( + {topLeft: 4.5, topRight: 4.5, bottomLeft: 4.5, bottomRight: 4.5}); + }); + it('should support object values', function() { + expect(toTRBLCorners({topLeft: 1, topRight: 2, bottomLeft: 3, bottomRight: 4})).toEqual( + {topLeft: 1, topRight: 2, bottomLeft: 3, bottomRight: 4}); + expect(toTRBLCorners({topLeft: 1.5, topRight: 2.5, bottomLeft: 3.5, bottomRight: 4.5})).toEqual( + {topLeft: 1.5, topRight: 2.5, bottomLeft: 3.5, bottomRight: 4.5}); + expect(toTRBLCorners({topLeft: '1', topRight: '2', bottomLeft: '3', bottomRight: '4'})).toEqual( + {topLeft: 1, topRight: 2, bottomLeft: 3, bottomRight: 4}); + }); + it('should fallback to 0 for invalid values', function() { + expect(toTRBLCorners({topLeft: 'foo', topRight: 'foo', bottomLeft: 'foo', bottomRight: 'foo'})).toEqual( + {topLeft: 0, topRight: 0, bottomLeft: 0, bottomRight: 0}); + expect(toTRBLCorners({topLeft: null, topRight: null, bottomLeft: null, bottomRight: null})).toEqual( + {topLeft: 0, topRight: 0, bottomLeft: 0, bottomRight: 0}); + expect(toTRBLCorners({})).toEqual( + {topLeft: 0, topRight: 0, bottomLeft: 0, bottomRight: 0}); + expect(toTRBLCorners('foo')).toEqual( + {topLeft: 0, topRight: 0, bottomLeft: 0, bottomRight: 0}); + expect(toTRBLCorners(null)).toEqual( + {topLeft: 0, topRight: 0, bottomLeft: 0, bottomRight: 0}); + expect(toTRBLCorners(undefined)).toEqual( + {topLeft: 0, topRight: 0, bottomLeft: 0, bottomRight: 0}); + }); + }); + describe('toPadding', function() { it ('should support number values', function() { expect(toPadding(4)).toEqual( From 5ed4d376040954fa2d98ebb03762e2b7b83a9847 Mon Sep 17 00:00:00 2001 From: Dan Manastireanu <498419+danmana@users.noreply.github.com> Date: Fri, 23 Oct 2020 17:34:40 +0300 Subject: [PATCH 2/8] feat: Implement borderRadius for bar charts --- src/controllers/controller.bar.js | 1 + src/elements/element.bar.js | 87 ++++++++++++++++++++++++++++--- 2 files changed, 81 insertions(+), 7 deletions(-) diff --git a/src/controllers/controller.bar.js b/src/controllers/controller.bar.js index 6ca1e4f6795..128f0fc033d 100644 --- a/src/controllers/controller.bar.js +++ b/src/controllers/controller.bar.js @@ -521,6 +521,7 @@ BarController.defaults = { 'borderColor', 'borderSkipped', 'borderWidth', + 'borderRadius', 'barPercentage', 'barThickness', 'base', diff --git a/src/elements/element.bar.js b/src/elements/element.bar.js index 5e4f04ab284..c7964d0230c 100644 --- a/src/elements/element.bar.js +++ b/src/elements/element.bar.js @@ -1,5 +1,6 @@ import Element from '../core/core.element'; -import {toTRBL} from '../helpers/helpers.options'; +import {toTRBL, toTRBLCorners} from '../helpers/helpers.options'; +import {PI, HALF_PI} from '../helpers/helpers.math'; /** * Helper function to get the bounds of the bar regardless of the orientation @@ -81,24 +82,46 @@ function parseBorderWidth(bar, maxW, maxH) { }; } +function parseBorderRadius(bar, maxW, maxH) { + const value = bar.options.borderRadius; + const o = toTRBLCorners(value); + const maxR = Math.min(maxW, maxH); + const skip = parseBorderSkipped(bar); + + 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) + }; +} + function boundingRects(bar) { const bounds = getBarBounds(bar); const width = bounds.right - bounds.left; const height = bounds.bottom - bounds.top; const border = parseBorderWidth(bar, width / 2, height / 2); + const radius = parseBorderRadius(bar, width / 2, height / 2); return { outer: { x: bounds.left, y: bounds.top, w: width, - h: height + h: height, + radius }, inner: { x: bounds.left + border.l, y: bounds.top + border.t, w: width - border.l - border.r, - h: height - border.t - border.b + h: height - border.t - border.b, + radius: { + topLeft: Math.max(0, radius.topLeft - Math.max(border.t, border.l)), + topRight: Math.max(0, radius.topRight - Math.max(border.t, border.r)), + bottomLeft: Math.max(0, radius.bottomLeft - Math.max(border.b, border.l)), + bottomRight: Math.max(0, radius.bottomRight - Math.max(border.b, border.r)), + } } }; } @@ -114,6 +137,52 @@ function inRange(bar, x, y, useFinalPosition) { && (skipY || y >= bounds.top && y <= bounds.bottom); } +function hasRadius(radius) { + return radius.topLeft !== 0 || radius.topRight !== 0 || radius.bottomLeft !== 0 || radius.bottomRight !== 0; +} + +/** + * Add a path of a rectangle with rounded corners to the current sub-path + * @param {CanvasRenderingContext2D} ctx Context + * @param {*} rect Bounding rect + */ +function addRoundedRectPath(ctx, rect) { + const {x, y, w, h, radius} = rect; + + // top left arc + ctx.arc(x + radius.topLeft, y + radius.topLeft, radius.topLeft, -HALF_PI, PI, true); + + // line from top left to bottom left + ctx.lineTo(x, y + h - radius.bottomLeft); + + // bottom left arc + ctx.arc(x + radius.bottomLeft, y + h - radius.bottomLeft, radius.bottomLeft, PI, HALF_PI, true); + + // line from bottom left to bottom right + ctx.lineTo(x + w - radius.bottomRight, y + h); + + // bottom right arc + ctx.arc(x + w - radius.bottomRight, y + h - radius.bottomRight, radius.bottomRight, HALF_PI, 0, true); + + // line from bottom right to top right + ctx.lineTo(x + w, y + radius.topRight); + + // top right arc + ctx.arc(x + w - radius.topRight, y + radius.topRight, radius.topRight, 0, -HALF_PI, true); + + // line from top right to top left + ctx.lineTo(x + radius.topLeft, y); +} + +/** + * Add a path of a rectangle to the current sub-path + * @param {CanvasRenderingContext2D} ctx Context + * @param {*} rect Bounding rect + */ +function addNormalRectPath(ctx, rect) { + ctx.rect(rect.x, rect.y, rect.w, rect.h); +} + export default class BarElement extends Element { constructor(cfg) { @@ -133,20 +202,23 @@ export default class BarElement extends Element { draw(ctx) { const options = this.options; const {inner, outer} = boundingRects(this); + const addRectPath = hasRadius(outer.radius) ? addRoundedRectPath : addNormalRectPath; ctx.save(); if (outer.w !== inner.w || outer.h !== inner.h) { ctx.beginPath(); - ctx.rect(outer.x, outer.y, outer.w, outer.h); + addRectPath(ctx, outer); ctx.clip(); - ctx.rect(inner.x, inner.y, inner.w, inner.h); + addRectPath(ctx, inner); ctx.fillStyle = options.borderColor; ctx.fill('evenodd'); } + ctx.beginPath(); + addRectPath(ctx, inner); ctx.fillStyle = options.backgroundColor; - ctx.fillRect(inner.x, inner.y, inner.w, inner.h); + ctx.fill(); ctx.restore(); } @@ -183,7 +255,8 @@ BarElement.id = 'bar'; */ BarElement.defaults = { borderSkipped: 'start', - borderWidth: 0 + borderWidth: 0, + borderRadius: 0 }; /** From 8051c50cc10cc355ec3e8af74fd6c9c83eb6900d Mon Sep 17 00:00:00 2001 From: Dan Manastireanu <498419+danmana@users.noreply.github.com> Date: Fri, 23 Oct 2020 21:43:01 +0300 Subject: [PATCH 3/8] chore: add demo of bar charts with border radius --- samples/charts/bar/border-radius.html | 147 ++++++++++++++++++++++++++ samples/samples.js | 3 + 2 files changed, 150 insertions(+) create mode 100644 samples/charts/bar/border-radius.html diff --git a/samples/charts/bar/border-radius.html b/samples/charts/bar/border-radius.html new file mode 100644 index 00000000000..b2abcfa16eb --- /dev/null +++ b/samples/charts/bar/border-radius.html @@ -0,0 +1,147 @@ + + + + + Bar Chart + + + + + + +
+ +
+ + + + + + + + + diff --git a/samples/samples.js b/samples/samples.js index 57cac6b3adf..eb569df52e7 100644 --- a/samples/samples.js +++ b/samples/samples.js @@ -22,6 +22,9 @@ }, { title: 'Floating', path: 'charts/bar/float.html' + }, { + title: 'Border Radius', + path: 'charts/bar/border-radius.html' }] }, { title: 'Line charts', From 38f130f54c8b861e68b1611be97662bbfdc2fa96 Mon Sep 17 00:00:00 2001 From: Dan Manastireanu <498419+danmana@users.noreply.github.com> Date: Fri, 23 Oct 2020 22:44:16 +0300 Subject: [PATCH 4/8] chore: document bar borderRadius --- docs/docs/charts/bar.mdx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/docs/charts/bar.mdx b/docs/docs/charts/bar.mdx index eeb39d9284f..2a96460a1fe 100644 --- a/docs/docs/charts/bar.mdx +++ b/docs/docs/charts/bar.mdx @@ -83,11 +83,13 @@ the color of the bars is generally set this way. | [`borderColor`](#styling) | [`Color`](../general/colors.md) | Yes | Yes | `'rgba(0, 0, 0, 0.1)'` | [`borderSkipped`](#borderskipped) | `string` | Yes | Yes | `'start'` | [`borderWidth`](#borderwidth) | number|object | Yes | Yes | `0` +| [`borderRadius`](#borderradius) | number|object | Yes | Yes | `0` | [`clip`](#general) | number|object | - | - | `undefined` | [`data`](#data-structure) | `object[]` | - | - | **required** | [`hoverBackgroundColor`](#interactions) | [`Color`](../general/colors.md) | Yes | Yes | `undefined` | [`hoverBorderColor`](#interactions) | [`Color`](../general/colors.md) | Yes | Yes | `undefined` | [`hoverBorderWidth`](#interactions) | `number` | Yes | Yes | `1` +| [`hoverBorderRadius`](#interactions) | `number` | Yes | Yes | `0` | [`indexAxis`](#general) | `string` | `'x'` | The base axis for the dataset. Use `'y'` for horizontal bar. | [`label`](#general) | `string` | - | - | `''` | [`order`](#general) | `number` | - | - | `0` @@ -116,13 +118,14 @@ The style of each bar can be controlled with the following properties: | `borderColor` | The bar border color. | [`borderSkipped`](#borderskipped) | The edge to skip when drawing bar. | [`borderWidth`](#borderwidth) | The bar border width (in pixels). +| [`borderRadius`](#borderradius) | The bar border radius (in pixels). | `clip` | How to clip relative to chartArea. Positive value allows overflow, negative value clips that many pixels inside chartArea. `0` = clip at chartArea. Clipping can also be configured per side: `clip: {left: 5, top: false, right: -2, bottom: 0}` All these values, if `undefined`, fallback to the associated [`elements.bar.*`](../configuration/elements.md#bar-configuration) options. #### borderSkipped -This setting is used to avoid drawing the bar stroke at the base of the fill. +This setting is used to avoid drawing the bar stroke at the base of the fill, or disable the border radius. In general, this does not need to be changed except when creating chart types that derive from a bar chart. @@ -142,6 +145,10 @@ Options are: If this value is a number, it is applied to all sides of the rectangle (left, top, right, bottom), except [`borderSkipped`](#borderskipped). If this value is an object, the `left` property defines the left border width. Similarly, the `right`, `top`, and `bottom` properties can also be specified. Omitted borders and [`borderSkipped`](#borderskipped) are skipped. +#### borderRadius + +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. + ### Interactions The interaction with each bar can be controlled with the following properties: @@ -151,6 +158,7 @@ The interaction with each bar can be controlled with the following properties: | `hoverBackgroundColor` | The bar background color when hovered. | `hoverBorderColor` | The bar border color when hovered. | `hoverBorderWidth` | The bar border width when hovered (in pixels). +| `hoverBorderRadius` | The bar border radius when hovered (in pixels). All these values, if `undefined`, fallback to the associated [`elements.bar.*`](../configuration/elements.md#bar-configuration) options. From 0828c2ac0c7a489f059e921ce4d18553c9439639 Mon Sep 17 00:00:00 2001 From: Dan Manastireanu <498419+danmana@users.noreply.github.com> Date: Fri, 23 Oct 2020 22:56:00 +0300 Subject: [PATCH 5/8] chore: update typescript with bar borderRadius property --- types/elements/index.d.ts | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/types/elements/index.d.ts b/types/elements/index.d.ts index ce3c200a517..2558c4d1c69 100644 --- a/types/elements/index.d.ts +++ b/types/elements/index.d.ts @@ -263,9 +263,24 @@ export interface IBarOptions extends ICommonOptions { * @default 'start' */ borderSkipped: 'start' | 'end' | 'left' | 'right' | 'bottom' | 'top'; + + /** + * Border radius + * @default 0 + */ + borderRadius: number | IBorderRadius; } -export interface IBarHoverOptions extends ICommonHoverOptions {} +export interface IBorderRadius { + topLeft: number; + topRight: number; + bottomLeft: number; + bottomRight: number; +} + +export interface IBarHoverOptions extends ICommonHoverOptions { + hoverBorderRadius: number | IBorderRadius; +} export interface BarElement< T extends IBarProps = IBarProps, From 52954e05a0a1d75da2b712f90fb06a56704ea85b Mon Sep 17 00:00:00 2001 From: Dan Manastireanu <498419+danmana@users.noreply.github.com> Date: Sat, 24 Oct 2020 18:54:58 +0300 Subject: [PATCH 6/8] fix: fix horizontal borders test failing due to antialiasing --- .../controller.bar/horizontal-borders.png | Bin 4122 -> 4201 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/test/fixtures/controller.bar/horizontal-borders.png b/test/fixtures/controller.bar/horizontal-borders.png index 1cd6913acfc05dee86e993471c3b7abb9302f706..73adeead5619ec9a0b636a129912c78659d33617 100644 GIT binary patch literal 4201 zcmeHLd010d7QgT11+x?l0@ccv+DZ^D#Hs^|$b%y2uvSzkMNCHuE>sXvwm@FA!f0Jk zS_+jVj%}sNRuBaX@iNr6LWBGzgM8iOVcb?s2>TjZ_ z)GytFIbZ)aK~TkLQOXWltCe{&EBiY!O%5OIs@W;21 zpa0W5&}roE$4A`?;eA<5eA*JYZL*l$UBEnP;dFJn)Stxa8oI{2af`KL^`b0()O1Qp zN?U)8F;%C|P1%A<#_STy>CVnCCGpC%+ zZ$U$tRD?14%f;}DVUoiHYYo>5W!{8IeTAql8^NG>mJy|5s#jNAU}_}wBIy7N=*PFB8k(->}VI{RYI-P&qf)p+Amn~}kKN&Q%mp9hKM zeUZ|_3)n2{XUb6ig1eHno><Ft z%$?@S+#_aoKXj5LI~rOc$N0iU!U_Q3u2BP1`Fpam3B}$D55YWNWI3m4gsqh-dM?Hk z9r{o(=wn!JB?Yp~h~H^g~fNz2rh0|5u^75qTX@@9VtxijUhv^h414}F5v=eihMSJt=41Lk) zyLQVkC%l*e#4w=D;MiBD#s^z$Or|!xNxxUgcn8oYxBV&4nm+x)p2uMV0ICrLf{~)5=+kmUItA> zGdsXsom86wW;a(f9S)|0q$LbYokr*srN7Tf&IB(~n`L5CKKJP;Sx8xgVEa=hNy7i~oQHeaH;*#!4etNEcDcB`d3r2DWP&3Lkaw^XUo)O4FT1}iJj&~H<=BMT7g zQ+B09Gt*Xl>bM6$UfPdG+MCKCotBXa36HpkB0>Fk1_ziYZLG45Qid!BQ|^RQgYCsgZMF4yg5Wq40klK~1KC**kvwCAGBG z-(GwU)MyC35)576Nfv^;Ggb9KS8W4Abuph8*R5K!km}OQ&<+kw|B3V<`ToivR2X%` zZk04#+InyMWonS!3oUC!`i#IjwC;H|MSfiBMkCHG$UPch; zx{X$!Jo{bn7GP_O`wl7^PMgTMb;x&le)K@J&vJHOJE!B;+X6;cMCd2I!{T1;++{dT?Euuk{0;CCzv zE8oBSacJ_s)jjj!8lPo2{ICSW?*d|eVe7BWm2i`Ukt6qS%45kB?jl_PG07h~RiVP7 zBB#d4;4twA(wJW#Jew$qiMGRuP$=7>8+J9V%H`1Ak@LriD+Nmd;P@E)DLYEXZNQfujyfkl(cxL#Z5E7-(nd8%$zW7R?HVHQJtdv1==q zmyqtgUaPJg%4AJKHN?P&UiJ3bOk7XCa-8I4u9s*2gQh}?X1r>z1Vq-AY$*bN#W{ zkDB!NoJZl*@F~KEB(ZXem*6(A7&$iX|3n{~Y|~B;e^Z9S&jt@<2BX72DwI~EU8wUd zwh;jCDDnN>WtHgi>7tRK|LxrF`OjNZFSt^#Pa2;ybR8Udp|ic7uGJ0jgEafyFV*ss nA`Rf~*7Hs{>p0nJq-I2^J`|YgxfQ}+2x!APZ`VQ>(ZPQLRa?}g literal 4122 zcmeHKX;@QN8a{V1+!!D!p)A5c5Nm3s5K3he!*xXm1*=xDY*E`{E0Q1-5HY!7aI}RM zryUCl2(4PCYJq93hBdcV(N+%91>(Pf5ChJfW?OOYqkJ@!mkJ*STI~Wb~_dTZpVf-E4QUa54ASD^Gr8V z*Wr{$E2)LPrLi47EpMlY9LmVg=i`|woee9K+`hAvu5Vd6+!Yt8O2%Iz9B;T?>Dzd@ zZWcNJ;M~ag$T$8a=i7Sc&P1PY9v1rOgD8NYIMIcl>ayZ&G(EJ~0ZW7#I&w?0B$fK+W9;F3#kN-L?JSFO`CfSWS|D>nGkIqua3PbEI)NW?DrdjRAl~elb;4 z;tdKgbSHup@2Eu@2U|hoas-`%P#P22f%-NJP{W6~x!M3>6bPi9Gd5HrDR9$RkYA)@ z#Xna0fHWaVJ5V}%5qM}uI{-6?NhvrP3%55pY(cfLd0Li0&N8QTh?Yeo0%ma!TzsB}A_No2K{X-+1Wz|R)}RRbNT}_p&!6)6bnZD#^i<%c!9XmIDVD~H4}tOa z;dWE)lI(<|{4~@I-GxX`d2-E8STk^is{FeWRjEffcu^#}g1P zHHrMrN98guRT5excj(PusBiU{*sn7b1_u<1R0R|-wcQ;(Zd_5X-EY9yCit`czT?&0Mw zMy|?-h33Ch#WS)Q+#<*;;iRlG(wsM61iVityBG-zZU_;D+74$LbHpu{fVg!-tBe#T zaYC5FTG+-M_czkue)faI#4VwyE>+wfRpE-_mv|y@X>+m#`oVl%l=cIg&Pzlb#Bv-p zXobK2bn27G-OjJq_)M*(=rUJry-)k>r#?O`nfa+f^Aa^-c%u>p14 zUt6d(%rghVOrUhYXcs{vtS_xFc%vo|HwQ;D$WR(QdduAt!DxE{si^}pg5YVN2eA~n znFhp%?k^~dI6523Yh|%e0>=Gw^C&68usAbDQgnTNZjAg%kR?uB#A~Pf&$mZf&1|AR zYdialR*LHFj$tc{mr6iVRc--YZ`bK+juS7=GpAl3*}{kFKt|Hws52OChD6i559wnk z!MXUU_xY-H4qb#LTM}#Fz@E`vd=&vz0#TMi-WZDBZ`2%tn*?Ouv@N83pnm*1)5b{5 z6-Xbd^zYag4q)2BAHNtOtOVs{^gvTtdqkQUi{9c_fvgoxQQVK+ zFdvh=eX@aX^kUHI%m z0qk=)>~pS%KV+?cnSv_GvoTQ!1Iu52@yGBVJqh_8fKUo|XRr3D?ZO|xtCko+rlC>F zo4~GB_db6QN7t){+O$tOgUEax<$JcS4=omI`$~%RXLDcKh2rk$6sAcHuyP>yg#KeI;i$gyB}VLYmyCj zeh$~+lMasm*Ow+9(iTOALE1|8@H5gD&qn18LW{(6-h%>v#0)_+k$AZUJfWYUaa|Gd)H}E!S zb>Bmk<*)?znDJ^@P#_Avh5S-+=*IyV=I|Ruu;@Dz9A{YcLo;&r667doaW2v45KXSx zm)xB3M56k@SkK(qlwH8>_?REsoo zN2ZR1X7T^vegoVZ8n?Jj_Vs|897uxeIDSLPpZ8XI;}9 zpo47!n$(PD9M?<)Q$-G}Vic220iEVAT)k7t`Z`0`2ch}`bN3e!)Q3gR@>(n)#oOiX zsZ)$TJnTc(_n(FRya&BxqZnb4Rf@i5v<+HS^(;>_q(;)P#sC7E(v(lxkN;aR|nLd#>??JJmPpmjDkZqfZygw!8BiU180K@7TOy+a{~2SUr;qd!Ce6+yfisq zT@0o4&2z!+HdnYkA3a|CD!4$sH zqRMOL#=m9Jjw@6gvcd`bsW?l5O<`-8!HnTm~8!8ccxHzdBqh3@lyXUMqvNabFuyw%Y6oj%e!dz3`EO`&QE;(GlnbgSb_#ht25t@vV{JhOPHB zi;2O75-@m0+^}{9G@iS1mVak(>Mz|Ou|zP*drmF$Ox|!C^oD~UhXFUaHfYJ|w&M!@7Yx0*{l?wD7+bCiBhc8wG_}Q>FWKHQRNybm&hXFVM From eceadccf986e02c87a618391545d823bceba4e49 Mon Sep 17 00:00:00 2001 From: Dan Manastireanu <498419+danmana@users.noreply.github.com> Date: Mon, 26 Oct 2020 10:22:35 +0200 Subject: [PATCH 7/8] chore: fix code review remarks --- src/elements/element.bar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/elements/element.bar.js b/src/elements/element.bar.js index c7964d0230c..1375e9276ea 100644 --- a/src/elements/element.bar.js +++ b/src/elements/element.bar.js @@ -138,7 +138,7 @@ function inRange(bar, x, y, useFinalPosition) { } function hasRadius(radius) { - return radius.topLeft !== 0 || radius.topRight !== 0 || radius.bottomLeft !== 0 || radius.bottomRight !== 0; + return radius.topLeft || radius.topRight || radius.bottomLeft || radius.bottomRight; } /** From 0201cc341087557af5a57fedf08b00477d724783 Mon Sep 17 00:00:00 2001 From: Dan Manastireanu <498419+danmana@users.noreply.github.com> Date: Mon, 26 Oct 2020 10:34:44 +0200 Subject: [PATCH 8/8] chore: Add border-radius visual test --- test/fixtures/controller.bar/border-radius.js | 45 ++++++++++++++++++ .../fixtures/controller.bar/border-radius.png | Bin 0 -> 6007 bytes 2 files changed, 45 insertions(+) create mode 100644 test/fixtures/controller.bar/border-radius.js create mode 100644 test/fixtures/controller.bar/border-radius.png diff --git a/test/fixtures/controller.bar/border-radius.js b/test/fixtures/controller.bar/border-radius.js new file mode 100644 index 00000000000..67c579ff09a --- /dev/null +++ b/test/fixtures/controller.bar/border-radius.js @@ -0,0 +1,45 @@ +module.exports = { + threshold: 0.01, + config: { + type: 'bar', + data: { + labels: [0, 1, 2, 3, 4, 5], + datasets: [ + { + // option in dataset + data: [0, 5, 10, null, -10, -5], + borderWidth: 2, + borderRadius: 5 + }, + { + // option in element (fallback) + data: [0, 5, 10, null, -10, -5], + borderSkipped: false, + borderRadius: Number.MAX_VALUE + } + ] + }, + options: { + legend: false, + title: false, + indexAxis: 'y', + elements: { + bar: { + backgroundColor: '#AAAAAA80', + borderColor: '#80808080', + borderWidth: {bottom: 6, left: 15, top: 6, right: 15} + } + }, + scales: { + x: {display: false}, + y: {display: false} + } + } + }, + options: { + canvas: { + height: 256, + width: 512 + } + } +}; diff --git a/test/fixtures/controller.bar/border-radius.png b/test/fixtures/controller.bar/border-radius.png new file mode 100644 index 0000000000000000000000000000000000000000..68e7c0dd2916c1e46bf790706f3a00d634b0c011 GIT binary patch literal 6007 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 literal 0 HcmV?d00001