Conversation
Dropdown's menu semantics (role="menu"/"menuitem") don't fit picking a value for a form - that's an ARIA listbox pattern instead. Listbox renders a hidden form field and instantly updates the trigger's label on selection, ahead of any phx-change round-trip, while keeping the same Floating UI positioning and keyboard navigation as Dropdown. Also adds a demo docs page with live examples, Wallaby coverage, and an Introduction page entry.
phx:show-start/phx:hide-end fire asynchronously, and the inner listbox's display is mutated as part of that same async completion. A rapid close-then-reopen (or reopen-then-close) - e.g. keyboard users toggling quickly, or key-repeat/assistive tech firing events faster than a human would - can start overlapping show/hide sequences, and whichever one's completion lands last wins, regardless of which was triggered most recently. The listbox could then silently close itself right after being reopened. Both handlers now re-assert the inner listbox's visibility against our own synchronous state (the wrapper) so a stale completion corrects itself instead of clobbering a more recent action. Also guards handleEnterOrSpace against a stale data-focus attribute left over from a just-closed listbox, and tears down any previous autoUpdate loop before starting a new one to avoid leaking it when handleShowStart fires more than once for the same open. Verified with 30 consecutive runs of the keyboard toggle test (previously ~30% flaky) plus 3 full clean runs of the Listbox suite. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Every modal Wallaby fixture rendered <.modal_overlay /> with no CSS class, so it was an empty div with zero rendered height - genuinely invisible to Selenium's visibility check regardless of its `display` value, which is why "expected overlay visible" assertions failed consistently (100% reproducible in isolation, not a timing flake). Fixtures that also wrapped .modal_panel in unstyled divs had a second, compounding issue: once the overlay correctly got `position: fixed` (covering the full viewport), it started painting above the panel's *unpositioned* wrapper divs per normal CSS stacking rules (positioned elements paint above static ones regardless of DOM order) - silently swallowing clicks meant for buttons inside the panel, such as the close button. Fixed by giving each fixture's overlay and panel wrapper the same classes already shown in the moduledoc's own documented examples. No changes to lib/prima/modal.ex or assets/js/hooks/modal.js - this was a test-fixture authoring gap, not a component bug. Verified with multiple full runs of all four affected suites, both isolated (--max-cases 1) and under normal concurrency: 27/27 passing consistently, versus 100% reproducible failure before. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
"Orange" is always present in the option list, even before the
debounced (200ms) async search actually runs - an empty search
matches every option server-side. So the test's assertion that
"Orange" exists didn't prove the search for "Orange" had completed;
under CPU load (a slow CI runner, or a stress-tested local machine),
Enter could fire before the debounce/round-trip landed, selecting
whatever was still focused from the stale, unfiltered list instead.
Reproduced 100% (10/10) under manufactured CPU pressure, root-caused
via a debug trace showing addSelection("Cherry") when the test meant
to select "Orange". No bug in combobox.js - Enter correctly selects
whatever's focused; the test had no signal that the filtered results
had actually arrived. Fixed by waiting for the option count to narrow
to the single match "Orange" produces among the fixture's options,
which only becomes true once the debounced search has completed.
Verified with 15 consecutive runs under CPU pressure (previously
reproduced instantly) plus a clean full run of the suite.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Same root cause as the Listbox fix (6f6bc49): phx:show-start/phx:hide-end fire asynchronously, and the inner menu's display is mutated as part of that same async completion. A rapid close-then-reopen - e.g. Escape immediately followed by Enter/Space, exactly what "focus returns to trigger button when dropdown closes" does - can call execJS(show) and execJS(hide) back-to-back before the previous call's internal steps finish, so their completions interleave and whichever lands last wins, regardless of which was triggered most recently. The menu could then silently close itself right after being reopened. dropdown.js is what Listbox's hook was originally copied from, so it carried the identical gap. Applied the same fix: handleShowStart/ handleHideEnd now re-assert the inner menu's visibility against the wrapper (our own synchronous state) so a stale completion corrects itself, handleEnterOrSpace no longer trusts a stale data-focus left over from a just-closed menu, and any previous autoUpdate loop is torn down before a new one starts. Verified with 30 consecutive runs of the previously-failing test (reported as a fresh failure on a clean local run) plus 3 full clean runs of all 6 Dropdown Wallaby suites (54 features each). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follow-up to #25