From f075ab55fb51196265e6dd4f5bf5aeb920cf2838 Mon Sep 17 00:00:00 2001 From: Graham McIntire Date: Fri, 13 Mar 2026 13:38:45 -0500 Subject: [PATCH] Fix price chart destroyed by LiveView DOM patches on filter change LiveView's morphdom was replacing the canvas element inside the chart container whenever filters were toggled, destroying the Chart.js rendering context. Add phx-update="ignore" to the chart container so LiveView preserves JS-managed children after initial render. --- lib/ammoprices_web/live/caliber_live/show.ex | 1 + .../live/caliber_live/show_test.exs | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/lib/ammoprices_web/live/caliber_live/show.ex b/lib/ammoprices_web/live/caliber_live/show.ex index 7c207d0..f35e2f7 100644 --- a/lib/ammoprices_web/live/caliber_live/show.ex +++ b/lib/ammoprices_web/live/caliber_live/show.ex @@ -282,6 +282,7 @@ defmodule AmmopricesWeb.CaliberLive.Show do
diff --git a/test/ammoprices_web/live/caliber_live/show_test.exs b/test/ammoprices_web/live/caliber_live/show_test.exs index 31aaa8e..5fc4840 100644 --- a/test/ammoprices_web/live/caliber_live/show_test.exs +++ b/test/ammoprices_web/live/caliber_live/show_test.exs @@ -182,6 +182,22 @@ defmodule AmmopricesWeb.CaliberLive.ShowTest do assert has_element?(view, "#filter-casing-steel") end + test "chart container uses phx-update=ignore to survive DOM patches", %{ + conn: conn, + caliber: caliber, + retailer: retailer + } do + now = DateTime.truncate(DateTime.utc_now(), :second) + product = product_fixture(%{caliber: caliber, retailer: retailer, grain_weight: 115, in_stock: true}) + snapshot_fixture(%{product: product, price_per_round_cents: 28, in_stock: true, recorded_at: now}) + + {:ok, view, _html} = live(conn, "/calibers/#{caliber.slug}") + + # Chart container must have phx-update="ignore" so LiveView + # does not replace the canvas element managed by Chart.js + assert has_element?(view, "#price-chart-container[phx-update=ignore]") + end + test "receives real-time price updates via PubSub", %{conn: conn, caliber: caliber, retailer: retailer} do now = DateTime.truncate(DateTime.utc_now(), :second) product = product_fixture(%{caliber: caliber, retailer: retailer, in_stock: true})