host: relate/unrelate keep both lists in sync (add to current list, never blank the picker)
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 30s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 30s
Two reported bugs on the edit page's relation editor:
1. relating a candidate didn't add it to the current-relations list (the AJAX
relate just deleted the candidate row; the relation only showed after a reload);
2. removing a relation could blank the relate picker.
Fix (lib/host/blog.sx): both the candidate's relate form and a current relation's
remove form now target #rel-editor-<kind> with sx-swap=outerHTML, and the
relate/unrelate handlers return the re-rendered editor for that kind (current list +
a fresh picker). So one swap keeps BOTH lists in sync: the related post moves into
the current list and out of the (re-loaded) candidate pool; removing moves it back.
Gated on the SX-Target header, so a plain boosted form / no-JS POST (the is-a-tag
toggle) still redirects + re-renders #content.
Engine fix (web/orchestration.sx): handle-html-response's non-select branch called
post-swap on the OLD target, which an outerHTML swap has already REPLACED — so the
swapped-in content's triggers (here the re-rendered picker's "load") never bound and
the picker stayed empty. post-swap the swap result (the new node), mirroring the
sx-select branch. Recompiled orchestration.sxbc for the content-addressed client.
Tests:
- web/tests/test-relate-picker.sx: relating re-syncs the editor (post in current
list + picker re-loads); removing does likewise — both fail without the engine fix.
- lib/host/tests/blog.sx: relate/unrelate return the re-rendered editor fragment
(200, #rel-editor + picker), forms wire to #rel-editor-KIND/outerHTML, plain
boosted POST still 303.
- relate-picker.spec.js: the full in-page flow (relate adds to list, remove keeps
the picker, no reload) + persistence.
Verified: host conformance 277/277, web engine suite 8/8, run-picker-check 3/3,
run-spa-check 3/3.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -296,34 +296,63 @@
|
||||
(contains? (dream-resp-body (host-bl-app (host-bl-req "/alpha-post/relate-options"))) "rp-more")
|
||||
false)
|
||||
|
||||
;; -- unrelate: AJAX in-place row delete (regression: removing a related post must
|
||||
;; NOT clear the relate picker). The remove button is now an sx-post form that
|
||||
;; deletes just its own current-relation row (sx-target=#cur-…, sx-swap=delete),
|
||||
;; so #content is never re-rendered and the picker is left intact. --
|
||||
;; -- relate / unrelate keep BOTH lists in sync by re-rendering the kind's editor.
|
||||
;; Regressions: (1) relating a candidate must ADD it to the current-relations
|
||||
;; list (not just delete the candidate row); (2) removing must NOT clear the
|
||||
;; relate picker. Both the candidate's relate form and the remove form target
|
||||
;; #rel-editor-KIND with sx-swap=outerHTML; the handler returns the re-rendered
|
||||
;; editor, so the current list updates and the fresh picker re-loads. --
|
||||
(host/blog-relate! "alpha-post" "beta-post" "related")
|
||||
(host-bl-test "relation-editor remove button is an AJAX in-place delete"
|
||||
;; the editor wraps current list + picker in #rel-editor-KIND; remove re-renders it
|
||||
(host-bl-test "relation-editor wires remove to re-render the kind's editor"
|
||||
(let ((html (render-page (host/blog--relation-editor "alpha-post" "related"))))
|
||||
(list (contains? html "id=\"cur-related-beta-post\"") ;; row has a target id
|
||||
(list (contains? html "id=\"rel-editor-related\"") ;; the swap target
|
||||
(contains? html "sx-post=\"/alpha-post/unrelate\"") ;; AJAX, not plain post
|
||||
(contains? html "sx-target=\"#cur-related-beta-post\"")
|
||||
(contains? html "sx-swap=\"delete\"")))
|
||||
(contains? html "sx-target=\"#rel-editor-related\"")
|
||||
(contains? html "sx-swap=\"outerHTML\"")))
|
||||
(list true true true true))
|
||||
;; the AJAX remove (carries SX-Target) returns an empty 200 so only the row is
|
||||
;; swapped out — no redirect, no #content re-render that would blank the picker.
|
||||
(host-bl-test "unrelate (AJAX, SX-Target) returns an empty 200"
|
||||
;; the candidate's relate form targets the SAME editor (so relating re-renders it)
|
||||
(host-bl-test "picker candidate relate form re-renders the kind's editor"
|
||||
(let ((html (render-page (host/blog--picker-item "alpha-post" {:slug "gamma-post" :title "Gamma"} "related"))))
|
||||
(list (contains? html "sx-post=\"/alpha-post/relate\"")
|
||||
(contains? html "sx-target=\"#rel-editor-related\"")
|
||||
(contains? html "sx-swap=\"outerHTML\"")))
|
||||
(list true true true))
|
||||
;; a POST request to a /:slug/… route, with the :slug route param populated (which
|
||||
;; the route matcher would set) plus headers + a form body.
|
||||
(define host-bl-relreq
|
||||
(fn (slug action headers other kind)
|
||||
(merge (dream-request "POST" (str "/" slug "/" action) headers
|
||||
(str "other=" other "&kind=" kind))
|
||||
{:params {:slug slug}})))
|
||||
;; the AJAX remove (carries SX-Target) returns the re-rendered editor fragment (200,
|
||||
;; with the #rel-editor wrapper + the picker) — not an empty body or a redirect.
|
||||
(host-bl-test "unrelate (AJAX, SX-Target) returns the re-rendered editor fragment"
|
||||
(let ((resp (host/blog-unrelate-submit
|
||||
(dream-request "POST" "/alpha-post/unrelate"
|
||||
{:sx-request "true" :sx-target "#cur-related-beta-post"}
|
||||
"other=beta-post&kind=related"))))
|
||||
(list (dream-status resp) (dream-resp-body resp)))
|
||||
(list 200 ""))
|
||||
(host-bl-relreq "alpha-post" "unrelate"
|
||||
{:sx-request "true" :sx-target "#rel-editor-related"}
|
||||
"beta-post" "related"))))
|
||||
(list (dream-status resp)
|
||||
(contains? (dream-resp-body resp) "rel-editor-related")
|
||||
(contains? (dream-resp-body resp) "relate-picker")))
|
||||
(list 200 true true))
|
||||
;; relate (AJAX, SX-Target) likewise returns the editor with the new relation listed
|
||||
(host/blog-unrelate! "alpha-post" "gamma-post" "related") ;; clean state
|
||||
(host-bl-test "relate (AJAX, SX-Target) returns the editor showing the new relation"
|
||||
(let ((resp (host/blog-relate-submit
|
||||
(host-bl-relreq "alpha-post" "relate"
|
||||
{:sx-request "true" :sx-target "#rel-editor-related"}
|
||||
"gamma-post" "related"))))
|
||||
(list (dream-status resp)
|
||||
(contains? (dream-resp-body resp) "/gamma-post/"))) ;; now in the current list
|
||||
(list 200 true))
|
||||
(host/blog-unrelate! "alpha-post" "gamma-post" "related")
|
||||
;; a plain boosted form / no-JS POST (no SX-Target) still redirects + re-renders,
|
||||
;; so the is-a-tag toggle and graceful degradation are unaffected.
|
||||
(host/blog-relate! "alpha-post" "beta-post" "related")
|
||||
(host-bl-test "unrelate (plain boosted / no-JS, no SX-Target) still redirects"
|
||||
(dream-status (host/blog-unrelate-submit
|
||||
(dream-request "POST" "/alpha-post/unrelate"
|
||||
{:sx-request "true"} "other=beta-post&kind=related")))
|
||||
(host-bl-relreq "alpha-post" "unrelate"
|
||||
{:sx-request "true"} "beta-post" "related")))
|
||||
303)
|
||||
(host/blog-unrelate! "alpha-post" "beta-post" "related")
|
||||
(host/blog-put! "hint-post" "Hint Post" "(p \"h\")" "published")
|
||||
|
||||
Reference in New Issue
Block a user