The panel that closed itself
I made it so posts can live in multiple spaces: An “Also show in” panel of checkboxes lets it appear in others. A lot of checkboxes is a lot of form, so that added complexity.
<details class="post-form__also-interests">
<summary><%= t("Also show in") %></summary>
<div class="post-form__also-interests-options">
<%= for interest <- @also_interest_options do %>
...
I found the bug while using the picker myself making the discussion thread for the previous post here. Open the panel, check a box, and the panel snaps shut. Open it again, check another box, shut again. Collapsed, it gives no sign anything is selected at all. This was a problem.
The bug #
The last panel bug on this blog was a race — three timers and a duplicate event, reproducible twice in many runs. This one is the opposite: perfectly deterministic, wrong every single time, and visible the moment you know where to look.
So reproducible it was (relatively) easy to write a smoke test for it.
A <details> element’s open attribute is written by the browser when the user clicks the summary. The server never hears about it. Meanwhile the composer is a LiveView form with live validation — every keystroke and every checkbox change makes a round trip, the server re-renders, and the patcher re-syncs the DOM to the server’s HTML. The server’s HTML has no open attribute, so the patcher removes it. Typing a single letter in the title field also closed the panel.
The fix #
Give the boolean to the server.
<button type="button" phx-click="toggle_also_interests"
aria-expanded={to_string(@also_picker_open?)}>
<%= t("Also show in") %>
<%= if names != [] do %>
<span><%= Enum.join(names, ", ") %></span>
<% end %>
</button>
<div hidden={!@also_picker_open?}>
... the checkboxes ...
</div>
The checkbox list is hidden when collapsed, not conditionally rendered. Inputs that leave the DOM leave the form, and a reader who collapses the panel before submitting doesn’t silently lose their picks.
Bug fixed, time for a beer.