diff --git a/frontend/src/metabase/css/dashboard.css b/frontend/src/metabase/css/dashboard.css index bfaaa02794393101cdf690faf01124d2efe43e6e..54685ac3faf7cb0c998a1ca5724ec6c0194fb40d 100644 --- a/frontend/src/metabase/css/dashboard.css +++ b/frontend/src/metabase/css/dashboard.css @@ -366,6 +366,16 @@ background-color: color(var(--color-bg-white) alpha(-86%)); } +.Dashboard--night text.value-label-outline { + stroke: var(--night-mode-card); +} + +.Dashboard text.value-label, +.Dashboard text.value-label-outline, +.Dashboard .LineAreaBarChart .dc-chart .axis text { + font-size: 12px; +} + .ScalarValue { font-weight: 900; font-size: 1.8rem; diff --git a/frontend/src/metabase/css/query_builder.css b/frontend/src/metabase/css/query_builder.css index 8e7c769f5240b5acca85c5cbea91707417386661..ff08afdcd93b8052137454975021e0b3ce2d87f5 100644 --- a/frontend/src/metabase/css/query_builder.css +++ b/frontend/src/metabase/css/query_builder.css @@ -692,23 +692,3 @@ .ParameterValuePickerNoPopover input::-webkit-input-placeholder { color: var(--color-text-medium); } - -text.value-label-outline { - font-weight: 900; - stroke-width: 4px; - stroke: var(--color-text-white); -} - -text.value-label { - fill: var(--color-text-dark); - font-weight: 900; -} - -text.value-label-outline, -text.value-label { - pointer-events: none; -} - -.Dashboard--night text.value-label-outline { - stroke: var(--night-mode-card); -} diff --git a/frontend/src/metabase/visualizations/components/LineAreaBarChart.css b/frontend/src/metabase/visualizations/components/LineAreaBarChart.css index 2b4cacc6b847c4eb22b3099d7d83e647fab125e0..340c87758df31cf3d79a93c33127fcb5b72948c3 100644 --- a/frontend/src/metabase/visualizations/components/LineAreaBarChart.css +++ b/frontend/src/metabase/visualizations/components/LineAreaBarChart.css @@ -206,3 +206,19 @@ .LineAreaBarChart .dc-chart .trend .line { stroke-dasharray: 5, 5; } + +text.value-label-outline, +text.value-label { + pointer-events: none; +} + +text.value-label-outline { + font-weight: 800; + stroke-width: 4px; + stroke: var(--color-text-white); +} + +text.value-label { + fill: var(--color-text-dark); + font-weight: 800; +} diff --git a/frontend/src/metabase/visualizations/lib/LineAreaBarPostRender.js b/frontend/src/metabase/visualizations/lib/LineAreaBarPostRender.js index 910a357ae1237546cabeb32d4923785eb676abd3..3edd8f8c189426b109f9dda05fe4a439370d1341 100644 --- a/frontend/src/metabase/visualizations/lib/LineAreaBarPostRender.js +++ b/frontend/src/metabase/visualizations/lib/LineAreaBarPostRender.js @@ -262,15 +262,17 @@ function onRenderValueLabels(chart, formatYValue, [data]) { // Update `data` to use named x/y and include `showLabelBelow`. // We need to do that before data is filtered to show every nth value. - data = data.map(([x, y], i) => { - const isLocalMin = - // first point or prior is greater than y - (i === 0 || data[i - 1][1] > y) && - // last point point or next is greater than y - (i === data.length - 1 || data[i + 1][1] > y); - const showLabelBelow = isLocalMin && display === "line"; - return { x, y, showLabelBelow }; - }); + data = data + .map(([x, y], i) => { + const isLocalMin = + // first point or prior is greater than y + (i === 0 || data[i - 1][1] > y) && + // last point point or next is greater than y + (i === data.length - 1 || data[i + 1][1] > y); + const showLabelBelow = isLocalMin && display === "line"; + return { x, y, showLabelBelow }; + }) + .filter(d => display !== "bar" || d.y !== 0); const formattingSetting = chart.settings["graph.label_value_formatting"]; let compact; diff --git a/frontend/src/metabase/visualizations/lib/apply_axis.js b/frontend/src/metabase/visualizations/lib/apply_axis.js index 006b4c58c4ffc86f043225ea375fbc19210a70ef..ab07a1688d10512e3d24dcad8946b178e5cf2dfc 100644 --- a/frontend/src/metabase/visualizations/lib/apply_axis.js +++ b/frontend/src/metabase/visualizations/lib/apply_axis.js @@ -369,6 +369,33 @@ export function applyChartYAxis(chart, series, yExtent, axisName) { scale = d3.scale.linear(); } + // This makes non-zero bar values take up at least one pixel. + // Ideally, we would just pass a custom interpolate factory to `interpolate`. + // However, dc.js passes its own after we give it the scael, so instead we + // overwrite the scale's interpolate method. That let's us use theirs but + // special case values withing one pixel of the edge. + if (series.every(s => s.card.display === "bar")) { + const _interpolate = scale.interpolate.bind(scale); + scale.interpolate = customInterpolatorFactory => + _interpolate((a, b) => { + // dc.js uses a rounding interpolator. We want to use the factory they + // pass in, but we also need to create d3's default interpolator. We use + // that to see when a value is between 0 and 1. If we just looked at the + // rounded value, 0.49 would round to 0 and we wouldn't bump it up to 1. + const custom = customInterpolatorFactory(a, b); + const unrounded = d3.interpolate(a, b); + return t => { + const value = unrounded(t); + const onePixelUp = custom(0) - 1; + // y goes from top to bottom, so "onePixelUp" is actually the largest value + if (onePixelUp < value && value < unrounded(0)) { + return onePixelUp; + } + return custom(t); + }; + }); + } + scale.clamp(true); if (axis.setting("auto_range")) {