Merge remote-tracking branch 'origin/main' into HEAD

# Conflicts:
#	app/javascript/controllers/sankey_chart_controller.js
#	app/javascript/controllers/time_series_chart_controller.js
This commit is contained in:
Guillem Arias
2026-06-03 08:08:26 +02:00
46 changed files with 2204 additions and 457 deletions

View File

@@ -1,6 +1,7 @@
import { Controller } from "@hotwired/stimulus";
import * as d3 from "d3";
import { sankey } from "d3-sankey";
import { CHART_TOOLTIP_CLASSES } from "utils/chart_tooltip";
import { sankeyNodeHasChildren, zoomSankeyData } from "utils/sankey_zoom";
// Connects to data-controller="sankey-chart"
@@ -509,10 +510,9 @@ export default class extends Controller {
this.tooltip = d3
.select(dialog || document.body)
.append("div")
.attr(
"class",
"bg-container text-primary text-sm font-sans absolute p-3 rounded-xl shadow-lg shadow-border-xs pointer-events-none z-50 top-0 privacy-sensitive",
)
// Shared visual contract + this chart's positioning class; opacity is
// toggled via inline style below.
.attr("class", `${CHART_TOOLTIP_CLASSES} top-0`)
.style("opacity", 0)
.style("pointer-events", "none");
}

View File

@@ -1,5 +1,6 @@
import { Controller } from "@hotwired/stimulus";
import * as d3 from "d3";
import { CHART_TOOLTIP_CLASSES } from "utils/chart_tooltip";
const parseLocalDate = d3.timeParse("%Y-%m-%d");
@@ -287,10 +288,8 @@ export default class extends Controller {
this._d3Tooltip = d3
.select(`#${this.element.id}`)
.append("div")
.attr(
"class",
"bg-container text-primary text-sm font-sans absolute p-3 rounded-xl shadow-lg shadow-border-xs pointer-events-none opacity-0 top-0 z-50 privacy-sensitive",
);
// Shared visual contract + this chart's initial-hidden / positioning classes.
.attr("class", `${CHART_TOOLTIP_CLASSES} opacity-0 top-0`);
}
_trackMouseForShowingTooltip() {

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-3 rounded-xl shadow-lg shadow-border-xs 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;
}