fix(goals/chart): redraw on turbo:render so the chart survives morph navigation

Repro: index -> goal show (chart drawn) -> open edit modal in turbo
frame -> pick a new icon -> submit. Server responds with
turbo_stream.action(:redirect, goal_path). Turbo morphs the show
page, wiping the chart container's children, but Stimulus' connect()
isn't re-run on the morphed element so _draw never fires again. The
ResizeObserver doesn't help — the container's box dimensions are
unchanged.

Listen for turbo:render and turbo:frame-load on document and re-draw
when the container's SVG is missing. Cheap idempotent check
(querySelector('svg')) — no-op if the chart is already there.

Listeners cleaned up in disconnect().

Verified: same flow now lands on the show page with the chart fully
rendered (23 SVG children).
This commit is contained in:
Guillem Arias
2026-05-11 21:52:56 +02:00
parent d633215529
commit a69b84f572

View File

@@ -36,12 +36,26 @@ export default class extends Controller {
attributeFilter: ["data-theme"],
});
}
// After a Turbo render (eg. after saving the goal from the edit modal
// and redirecting back to show), the chart container can be left empty
// — its children are wiped by the morph but connect() was already
// called and ResizeObserver doesn't fire because the size didn't
// change. Listen for the render event so we redraw when needed.
this._onTurboRender = () => {
if (!this.element.querySelector("svg")) this._draw();
};
document.addEventListener("turbo:render", this._onTurboRender);
document.addEventListener("turbo:frame-load", this._onTurboRender);
}
disconnect() {
window.removeEventListener("resize", this._resize);
this._observer?.disconnect();
this._themeObserver?.disconnect();
if (this._onTurboRender) {
document.removeEventListener("turbo:render", this._onTurboRender);
document.removeEventListener("turbo:frame-load", this._onTurboRender);
}
}
_draw() {