From 1be56095721fb6a63ec73510380c63adbff73967 Mon Sep 17 00:00:00 2001 From: Tony Kay Date: Sun, 5 Apr 2026 14:35:29 +0200 Subject: [PATCH] BUGFIX: resolve-route-and-navigate! drops params on cross-chart targets (#30) When decode-url could not match a leaf to a local route element (cross-chart target), the fallback branch discarded URL query params. Fixed by returning params from the codec even without a leaf match, and passing them through in the fallback navigation path. Co-Authored-By: Claude Opus 4.6 (1M context) --- Guide.adoc | 3 ++- .../statecharts/integration/fulcro/routing.cljc | 14 +++++++++----- .../integration/fulcro/routing/url_codec.cljc | 4 +++- .../fulcro/routing/url_codec_transit.cljc | 2 +- .../fulcro/routing/simulated_history_spec.cljc | 4 ++-- 5 files changed, 17 insertions(+), 10 deletions(-) diff --git a/Guide.adoc b/Guide.adoc index 9f09b51..b1424ba 100644 --- a/Guide.adoc +++ b/Guide.adoc @@ -1627,7 +1627,8 @@ URL encoding and decoding is handled by the `URLCodec` protocol defined in `rout "Given a context map, return a URL path string (with query if needed). Context keys: :segments, :params, :route-elements") (decode-url [this href route-elements] - "Given a URL string and route-elements map, return {:leaf-id :params} or nil.")) + "Given a URL string and route-elements map, return {:leaf-id :params}, or nil. + May return {:params } without :leaf-id for cross-chart targets.")) ----- ==== Default: TransitBase64Codec diff --git a/src/main/com/fulcrologic/statecharts/integration/fulcro/routing.cljc b/src/main/com/fulcrologic/statecharts/integration/fulcro/routing.cljc index 3d27ebd..d8b4ecf 100644 --- a/src/main/com/fulcrologic/statecharts/integration/fulcro/routing.cljc +++ b/src/main/com/fulcrologic/statecharts/integration/fulcro/routing.cljc @@ -986,8 +986,8 @@ [app elements-by-id provider codec] (let [href (ruh/current-href provider) decoded (ruc/decode-url codec href elements-by-id)] - (if decoded - (let [{:keys [leaf-id params]} decoded + (if-let [leaf-id (:leaf-id decoded)] + (let [{:keys [params]} decoded route-params (when params (reduce-kv (fn [acc _state-id state-params] (merge acc state-params)) @@ -996,12 +996,16 @@ leaf-id) ;; Codec didn't match directly — try reachable targets via deep search ;; (needed for cross-chart :route/reachable targets not in this chart's elements) - (let [segments (ruh/current-url-path href) - leaf-name (peek segments)] + (let [segments (ruh/current-url-path href) + leaf-name (peek segments) + route-params (when-let [params (:params decoded)] + (reduce-kv (fn [acc _state-id state-params] + (merge acc state-params)) + {} params))] (when leaf-name (when-let [{:keys [target-key child?]} (ruh/find-target-by-leaf-name-deep elements-by-id leaf-name)] (when child? - (route-to! app target-key {}) + (route-to! app target-key (or route-params {})) target-key))))))) (defn install-url-sync! diff --git a/src/main/com/fulcrologic/statecharts/integration/fulcro/routing/url_codec.cljc b/src/main/com/fulcrologic/statecharts/integration/fulcro/routing/url_codec.cljc index cda4344..202db85 100644 --- a/src/main/com/fulcrologic/statecharts/integration/fulcro/routing/url_codec.cljc +++ b/src/main/com/fulcrologic/statecharts/integration/fulcro/routing/url_codec.cljc @@ -33,7 +33,9 @@ Context keys: :segments (vector of state IDs), :params (map keyed by state-id), :route-elements (map of state-id -> element map).") (decode-url [this href route-elements] - "Given a URL string and route-elements map, return {:leaf-id :params }.")) + "Given a URL string and route-elements map, return {:leaf-id :params }, + or nil. May return {:params } without :leaf-id when params are present but the + leaf segment doesn't match a known route element (e.g. cross-chart targets).")) ;; --------------------------------------------------------------------------- ;; Path generation from active configuration (state -> URL) diff --git a/src/main/com/fulcrologic/statecharts/integration/fulcro/routing/url_codec_transit.cljc b/src/main/com/fulcrologic/statecharts/integration/fulcro/routing/url_codec_transit.cljc index fbb3cbd..f0027d4 100644 --- a/src/main/com/fulcrologic/statecharts/integration/fulcro/routing/url_codec_transit.cljc +++ b/src/main/com/fulcrologic/statecharts/integration/fulcro/routing/url_codec_transit.cljc @@ -143,7 +143,7 @@ (= (url-codec/element-segment element) leaf-name)) id)) route-elements))] - (when leaf-id + (when (or leaf-id params) {:leaf-id leaf-id :params params})))) diff --git a/src/test/com/fulcrologic/statecharts/integration/fulcro/routing/simulated_history_spec.cljc b/src/test/com/fulcrologic/statecharts/integration/fulcro/routing/simulated_history_spec.cljc index a752c05..b881f87 100644 --- a/src/test/com/fulcrologic/statecharts/integration/fulcro/routing/simulated_history_spec.cljc +++ b/src/test/com/fulcrologic/statecharts/integration/fulcro/routing/simulated_history_spec.cljc @@ -396,12 +396,12 @@ "decodes leaf matching by :route/segment" (:leaf-id decoded) => :page-b))) - (component "decode returns nil for unrecognized URL" + (component "decode returns nil for unrecognized URL without params" (let [codec (ruct/transit-base64-codec) route-elements {:page-a {:route/target 'com.example/PageA}} decoded (ruc/decode-url codec "/unknown/path" route-elements)] (assertions - "returns nil when no route element matches the leaf segment" + "returns nil when no route element matches and no params present" decoded => nil))) (component "single segment route"