Returns focus when a Plan picker closes - #172
Merged
Merged
Conversation
Closing a picker on the Plan page removed the control that held the focus, and a keyboard fell back to the document. Every way a picker closes now names the control the focus goes to: the row's "+" after Cancel or a refused pick in a row's picker, the row of the slot's block after Cancel or a refused pick in a slot's picker, and the new step's row after a pick that lands (the first new row on the page for a recipe). The page adds a hidden element on every close whose phx-mounted is JS.focus/1 naming that control, so the command runs after the patch has drawn it. The row's "+" and sentence buttons gain ids built from the block id. LiveView tests cover each closing path. Refs: se-9cpa
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.
Refs: se-9cpa
What
Closing a picker on the Plan page removed the control that held the focus, and a keyboard fell back to the document - the same loss the pickers already fix on opening with
JS.focus_first/0. Every way a picker closes now names the control the focus goes to:The page adds a hidden
plan-focus-return-<n>element on every close whosephx-mountedisJS.focus/1naming that control, so the command runs once the patch has drawn it; each close adds a new element, so the command runs again. The row's "+" and sentence buttons gain ids built from the block id (plan-add-,plan-step-). The selector quotes the id ([id="..."]) because a block id is opaque. The design is written in the Plan page's moduledoc, beside the passage on focus when a picker opens, and in the comment above the panel's picker.assets/js/app.jsis unchanged.Evidence
describe "focus when a picker closes", one per closing path: Cancel in a row's picker; Cancel in a slot's picker; a type pick in a row's picker; a recipe pick (the core "deadline" recipe, whose first new row heads the group's body rather than the armed gap); a pick in a slot's picker; a refused type pick and a refused recipe pick in a row's picker; a refused pick in a slot's picker; each close adding its own element; a close with no picker open moving nothing. Each asserts the hidden element'sphx-mountedisJS.focus/1on the control it names, and the path-specific ones assert that control is on the page. All run on the patron registration document.mix qualitygreen on the committed tree: 644 of 644 tests, 85.0% coverage, Credo and Dialyzer clean. The commit was made on the byte-identical tree the gate ran on.Review
Tier: gate (a reference-host page; no public function, option or wire shape a host reads; under the size threshold). In-turn review: the diff was re-read against the bead and its dated notes, which name Cancel, a pick and a refused pick. The closing handlers go through
close_picker/1("insert-close"and both"insert"refusal branches) orcommit_pick/2(both"insert"success branches); a read-only page still refuses all of them in its write-gate clause before either runs. The timing claim in the moduledoc (the command runs after the patch has drawn its target) was checked against phoenix_live_view 1.2.12's client:phx-mountedis run from the patch's after-added callbacks, which fire once the morph has finished. The moduledoc's "refused pick" sentence was checked against the three refusal arms: an unresolved gap or type, a recipe that does not land, and a session refusal.Provenance
JS.focus/1on a freshly added element rather than through a client hook, so the page needs no JavaScript of its own. Decided under the bead's acceptance by the worker, 2026-09-30.commit_pick/2has no test of its own: no payload tried reaches it (every palette type was tried at one row's gap in the patron registration document, and every one landed). It sends the focus where Cancel does.