From 034aa6e0bf51602c2eead080d45892cbb43c0ace Mon Sep 17 00:00:00 2001 From: Petru Paler Date: Sat, 24 Jan 2026 18:53:01 +0000 Subject: [PATCH] Fix facet pills replacing body instead of self on HTMX update Add hx_target="this" to the dsl_facet_pills container to prevent HTMX from inheriting hx_target="body" from the parent wrapper. Without this, clicking a facet pill would cause the facet refresh to replace the entire body with just the pills HTML, breaking forms on pages like /actions/outcome. Co-Authored-By: Claude Opus 4.5 --- src/animaltrack/web/templates/dsl_facets.py | 1 + tests/e2e/test_facet_pills.py | 36 +++++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/src/animaltrack/web/templates/dsl_facets.py b/src/animaltrack/web/templates/dsl_facets.py index 43381f1..9e9b2b8 100644 --- a/src/animaltrack/web/templates/dsl_facets.py +++ b/src/animaltrack/web/templates/dsl_facets.py @@ -71,6 +71,7 @@ def dsl_facet_pills( hx_get=htmx_url, hx_trigger=f"change from:#{filter_input_id} delay:600ms", hx_include=f"#{filter_input_id}", + hx_target="this", hx_swap="outerHTML", cls="space-y-3 mb-4", ) diff --git a/tests/e2e/test_facet_pills.py b/tests/e2e/test_facet_pills.py index f1bc7d7..6013e9c 100644 --- a/tests/e2e/test_facet_pills.py +++ b/tests/e2e/test_facet_pills.py @@ -122,6 +122,42 @@ class TestFacetPillsOnOutcomeForm: filter_input = page.locator("#filter") expect(filter_input).to_have_value("species:duck") + def test_facet_click_preserves_form_structure(self, page: Page, live_server): + """Clicking a facet pill should not replace the form with just pills. + + Regression test: Without hx_target="this" on the facet pills container, + HTMX inherits hx_target="body" from the parent and replaces the entire + page body with just the facet pills HTML. + """ + page.goto(f"{live_server.url}/actions/outcome") + expect(page.locator("body")).to_be_visible() + + # Verify form elements are visible before clicking facet + outcome_select = page.locator("#outcome") + expect(outcome_select).to_be_visible() + + filter_input = page.locator("#filter") + expect(filter_input).to_be_visible() + + # Click a facet pill + duck_pill = page.locator('[data-facet-field="species"][data-facet-value="duck"]') + expect(duck_pill).to_be_visible() + duck_pill.click() + + # Wait for HTMX to complete the facet refresh (600ms delay + network time) + # The facet pills use hx_trigger="change delay:600ms" so we must wait + page.wait_for_timeout(1000) + page.wait_for_load_state("networkidle") + + # Form elements should still be visible after facet pills refresh + # If this fails, the body was replaced with just the facet pills + expect(outcome_select).to_be_visible() + expect(filter_input).to_be_visible() + + # Verify the form can still be submitted (submit button visible) + submit_button = page.locator('button[type="submit"]') + expect(submit_button).to_be_visible() + class TestFacetPillsOnTagAddForm: """Test facet pills functionality on the tag add form."""