diff --git a/frontend/src/metabase/visualizations/lib/apply_axis.js b/frontend/src/metabase/visualizations/lib/apply_axis.js index c55936d1d50a4c3fe430d0190f2dd40c78633292..eead07d01c650173c8764e09c8646e87e87c9211 100644 --- a/frontend/src/metabase/visualizations/lib/apply_axis.js +++ b/frontend/src/metabase/visualizations/lib/apply_axis.js @@ -317,6 +317,17 @@ export function applyChartOrdinalXAxis( chart.x(d3.scale.ordinal().domain(xValues)).xUnits(dc.units.ordinal); } +// Sometimes tick marks are placed *just* off from zero. +// We still want to format these as "0" rather than "0.0000000000000018". +// But! We need to allow for real non-zero ticks at very small values, +// so we scale a tolerance to the extent of the yAxis. +// The tolerance is arbitrarily set to one millionth of the yExtent. +const TOLERANCE_TO_Y_EXTENT = 1e6; +export function maybeRoundValueToZero(value, [yMin, yMax]) { + const tolerance = Math.abs(yMax - yMin) / TOLERANCE_TO_Y_EXTENT; + return Math.abs(value) < tolerance ? 0 : value; +} + export function applyChartYAxis(chart, series, yExtent, axisName) { let axis; if (axisName !== "right") { @@ -358,11 +369,10 @@ export function applyChartYAxis(chart, series, yExtent, axisName) { axis.axis().tickFormat(value => Math.round(value * 100) + "%"); } else { const metricColumn = series[0].data.cols[1]; - axis - .axis() - .tickFormat(value => - formatValue(value, chart.settings.column(metricColumn)), - ); + axis.axis().tickFormat(value => { + value = maybeRoundValueToZero(value, yExtent); + return formatValue(value, chart.settings.column(metricColumn)); + }); } chart.renderHorizontalGridLines(true); adjustYAxisTicksIfNeeded(axis.axis(), chart.height()); diff --git a/frontend/test/metabase/visualizations/lib/apply_axis.unit.spec.js b/frontend/test/metabase/visualizations/lib/apply_axis.unit.spec.js index d8df889abe1e203b1c21c563a1288d4522f13996..fb0ee1a75d523041b1ff37cb5fa11843406f8163 100644 --- a/frontend/test/metabase/visualizations/lib/apply_axis.unit.spec.js +++ b/frontend/test/metabase/visualizations/lib/apply_axis.unit.spec.js @@ -1,4 +1,7 @@ -import { stretchTimeseriesDomain } from "metabase/visualizations/lib/apply_axis"; +import { + maybeRoundValueToZero, + stretchTimeseriesDomain, +} from "metabase/visualizations/lib/apply_axis"; import moment from "moment"; describe("visualization.lib.apply_axis", () => { @@ -39,4 +42,24 @@ describe("visualization.lib.apply_axis", () => { ]); }); }); + + describe("maybeRoundValueToZero", () => { + it("shouldn't change big values", () => { + const value = maybeRoundValueToZero(0.2, [-1, 1]); + + expect(value).toBe(0.2); + }); + + it("should snap small values to zero", () => { + const value = maybeRoundValueToZero(0.0000000000018, [-1, 1]); + + expect(value).toBe(0); + }); + + it("shouldn't snap small values to zero if the yExtent is small", () => { + const value = maybeRoundValueToZero(0.0000000000018, [-1e-20, 1e-20]); + + expect(value).toBe(0.0000000000018); + }); + }); });