mirror of
https://github.com/we-promise/sure.git
synced 2026-06-06 03:09:02 +00:00
feat(charts): give sankey and goal tooltips their missing context rows
Chart tooltips answer three stacked questions: context (what am I looking at), value (how much), relation (vs what). Time-series already had all three; the other two were missing rows. - Sankey links showed a bare "$X (Y%)" with no indication of which flow was hovered. Links now lead with a swatch-dotted "Source → Target" context line; nodes get the same dot + name treatment, tying the card to the ribbon color. The context builder escapes node names centrally (they're user-named categories). - Goal projection adds a tertiary relation line — "52% of $20K target" — computed from the payload the chart already carries, for both the saved and projected segments. Hidden when the goal has no positive target. Template is i18n-wired like the existing tooltip strings (goals.show.projection.tooltip_target_relation). Verified with Playwright against the running app: all three surfaces pass computed-style and content assertions.
This commit is contained in:
@@ -23,6 +23,7 @@ export default class extends Controller {
|
||||
todayLabel: { type: String, default: "Today" },
|
||||
projectedTemplate: { type: String, default: "Projected: {amount}" },
|
||||
savedTemplate: { type: String, default: "Saved: {amount}" },
|
||||
targetRelationTemplate: { type: String, default: "{percent}% of {target} target" },
|
||||
};
|
||||
|
||||
connect() {
|
||||
@@ -452,7 +453,25 @@ export default class extends Controller {
|
||||
tooltipDate.className = CHART_TOOLTIP_CONTEXT_CLASSES;
|
||||
const tooltipValue = document.createElement("div");
|
||||
tooltipValue.className = CHART_TOOLTIP_VALUE_CLASSES;
|
||||
tooltip.replaceChildren(tooltipDate, tooltipValue);
|
||||
// Relation line: where this value sits against the goal target. Tertiary
|
||||
// so the hierarchy stays date < value > relation; hidden when the goal
|
||||
// has no positive target to compare against.
|
||||
const tooltipRelation = document.createElement("div");
|
||||
tooltipRelation.className = "text-xs text-subdued mt-0.5";
|
||||
tooltip.replaceChildren(tooltipDate, tooltipValue, tooltipRelation);
|
||||
|
||||
const setRelation = (amount) => {
|
||||
const target = Number(data.target_amount) || 0;
|
||||
if (target <= 0 || !data.target_amount_short_label) {
|
||||
tooltipRelation.style.display = "none";
|
||||
return;
|
||||
}
|
||||
const percent = Math.round((amount / target) * 100);
|
||||
tooltipRelation.textContent = this.targetRelationTemplateValue
|
||||
.replace("{percent}", percent)
|
||||
.replace("{target}", data.target_amount_short_label);
|
||||
tooltipRelation.style.display = "";
|
||||
};
|
||||
|
||||
const overlay = svg
|
||||
.append("rect")
|
||||
@@ -505,6 +524,7 @@ export default class extends Controller {
|
||||
hoverProjDot.attr("cx", hoverX).attr("cy", y(projValue)).style("display", null);
|
||||
hoverSavedDot.style("display", "none");
|
||||
tooltipValue.textContent = this.projectedTemplateValue.replace("{amount}", this._fmtMoney(projValue, data.currency));
|
||||
setRelation(projValue);
|
||||
} else {
|
||||
// Saved segment: hoverDate is already snapped to nearest savedSeries
|
||||
// entry above, so reuse that entry directly instead of running
|
||||
@@ -513,6 +533,7 @@ export default class extends Controller {
|
||||
hoverSavedDot.attr("cx", x(savedPoint.date)).attr("cy", y(savedPoint.value)).style("display", null);
|
||||
hoverProjDot.style("display", "none");
|
||||
tooltipValue.textContent = this.savedTemplateValue.replace("{amount}", this._fmtMoney(savedPoint.value, data.currency));
|
||||
setRelation(savedPoint.value);
|
||||
}
|
||||
|
||||
tooltip.style.display = "block";
|
||||
|
||||
Reference in New Issue
Block a user