diff --git a/src/helpers/helpers.extras.ts b/src/helpers/helpers.extras.ts index dc34ecf07f9..beabb6d96f3 100644 --- a/src/helpers/helpers.extras.ts +++ b/src/helpers/helpers.extras.ts @@ -91,25 +91,41 @@ export function _getStartAndCountOfVisiblePoints(meta: ChartMeta<'line' | 'scatt let count = pointCount; if (meta._sorted) { - const {iScale, _parsed} = meta; + const {iScale, vScale, _parsed} = meta; + const spanGaps = meta.dataset ? meta.dataset.options ? meta.dataset.options.spanGaps : null : null; const axis = iScale.axis; const {min, max, minDefined, maxDefined} = iScale.getUserBounds(); if (minDefined) { - start = _limitValue(Math.min( + start = Math.min( // @ts-expect-error Need to type _parsed _lookupByKey(_parsed, axis, min).lo, // @ts-expect-error Need to fix types on _lookupByKey - animationsDisabled ? pointCount : _lookupByKey(points, axis, iScale.getPixelForValue(min)).lo), - 0, pointCount - 1); + animationsDisabled ? pointCount : _lookupByKey(points, axis, iScale.getPixelForValue(min)).lo); + if (spanGaps) { + const distanceToDefinedLo = (_parsed + .slice(0, start + 1) + .reverse() + .findIndex( + point => point[vScale.axis] || point[vScale.axis] === 0)); + start -= Math.max(0, distanceToDefinedLo); + } + start = _limitValue(start, 0, pointCount - 1); } if (maxDefined) { - count = _limitValue(Math.max( + let end = Math.max( // @ts-expect-error Need to type _parsed _lookupByKey(_parsed, iScale.axis, max, true).hi + 1, // @ts-expect-error Need to fix types on _lookupByKey - animationsDisabled ? 0 : _lookupByKey(points, axis, iScale.getPixelForValue(max), true).hi + 1), - start, pointCount) - start; + animationsDisabled ? 0 : _lookupByKey(points, axis, iScale.getPixelForValue(max), true).hi + 1); + if (spanGaps) { + const distanceToDefinedHi = (_parsed + .slice(end - 1) + .findIndex( + point => point[vScale.axis] || point[vScale.axis] === 0)); + end += Math.max(0, distanceToDefinedHi); + } + count = _limitValue(end, start, pointCount) - start; } else { count = pointCount - start; } diff --git a/test/specs/controller.line.tests.js b/test/specs/controller.line.tests.js index 368627d68a0..3924a79887d 100644 --- a/test/specs/controller.line.tests.js +++ b/test/specs/controller.line.tests.js @@ -1071,6 +1071,93 @@ describe('Chart.controllers.line', function() { expect(visiblePoints.length).toBe(6); }, 500); + it('should correctly calc _drawStart and _drawCount when first points beyond scale limits are null and spanGaps=true', async() => { + var chart = window.acquireChart({ + type: 'line', + data: { + labels: [0, 10, 20, 30, 40, 50], + datasets: [{ + data: [3, null, 2, 3, null, 1.5], + spanGaps: true, + tension: 0.4 + }] + }, + options: { + scales: { + x: { + type: 'linear', + min: 11, + max: 40, + } + } + } + }); + + chart.update(); + var controller = chart.getDatasetMeta(0).controller; + + expect(controller._drawStart).toBe(0); + expect(controller._drawCount).toBe(6); + }, 500); + + it('should correctly calc _drawStart and _drawCount when all points beyond scale limits are null and spanGaps=true', async() => { + var chart = window.acquireChart({ + type: 'line', + data: { + labels: [0, 10, 20, 30, 40, 50], + datasets: [{ + data: [null, null, 2, 3, null, null], + spanGaps: true, + tension: 0.4 + }] + }, + options: { + scales: { + x: { + type: 'linear', + min: 11, + max: 40, + } + } + } + }); + + chart.update(); + var controller = chart.getDatasetMeta(0).controller; + + expect(controller._drawStart).toBe(1); + expect(controller._drawCount).toBe(4); + }, 500); + + it('should correctly calc _drawStart and _drawCount when spanGaps=false', async() => { + var chart = window.acquireChart({ + type: 'line', + data: { + labels: [0, 10, 20, 30, 40, 50], + datasets: [{ + data: [3, null, 2, 3, null, 1.5], + spanGaps: false, + tension: 0.4 + }] + }, + options: { + scales: { + x: { + type: 'linear', + min: 11, + max: 40, + } + } + } + }); + + chart.update(); + var controller = chart.getDatasetMeta(0).controller; + + expect(controller._drawStart).toBe(1); + expect(controller._drawCount).toBe(4); + }, 500); + it('should not override tooltip title and label callbacks', async() => { const chart = window.acquireChart({ type: 'line',