refactor(charts): extract shared chart-tooltip className to one source (#2106)

Closes #2011.

time-series and sankey each created their cursor-following tooltip with a
duplicated className literal that had already drifted apart: time-series was
missing `text-primary` and `z-50`. Move the visual contract into
app/javascript/utils/chart_tooltip.js as CHART_TOOLTIP_CLASSES and have both
controllers reference it. Each keeps its own behavioural classes (time-series
its initial `opacity-0`; both `top-0`; sankey toggles opacity via inline
style). `privacy-sensitive` stays bundled so future copies can't drop it.

Also exports a createChartTooltip factory for the raw-DOM idiom.

goal_projection_chart_controller is not in main yet (it lands with the goals
work in #1798); it migrates to the same symbol there.
This commit is contained in:
Guillem Arias Fauste
2026-06-02 23:59:55 +02:00
committed by GitHub
parent e15ae5b23a
commit 998cfd61d6
3 changed files with 32 additions and 8 deletions

View File

@@ -0,0 +1,25 @@
// Single source of truth for the cursor-following tooltip used by the chart
// controllers (time-series, sankey, and goal-projection once it lands from the
// goals work). Keeping the visual contract here stops the bg / text / border /
// privacy-sensitive classes from drifting apart across the controllers, the way
// they had before (time-series was missing `text-primary` and `z-50`).
//
// This is the VISUAL contract only. Callers append their own behavioural
// classes (initial `opacity-0`, `top-0`, …) or set them via inline styles,
// because how each chart shows/hides and positions its tooltip differs.
//
// Not to be confused with DS::Tooltip — that is the info-icon hint primitive
// (bg-inverse, aria-describedby, anchored to a static trigger). This is a
// data-card surface created and updated inside D3 handler code.
export const CHART_TOOLTIP_CLASSES =
"bg-container text-primary text-sm font-sans absolute p-2 border border-secondary rounded-lg pointer-events-none z-50 privacy-sensitive";
// Convenience factory for the raw-DOM idiom (no d3.select). Creates a hidden
// tooltip div carrying the shared contract and appends it to `parent`.
export function createChartTooltip(parent) {
const tooltip = document.createElement("div");
tooltip.className = CHART_TOOLTIP_CLASSES;
tooltip.style.display = "none";
parent.appendChild(tooltip);
return tooltip;
}