Skip to content

Bind the popup's presets without waiting a tick - #14

Merged
Fivell merged 2 commits into
masterfrom
fix/bind-popup-synchronously
Oct 1, 2026
Merged

Fivell merged 2 commits into
masterfrom
fix/bind-popup-synchronously

Conversation

@Fivell

@Fivell Fivell commented Oct 1, 2026

Copy link
Copy Markdown
Member

Found while writing the first test suite (#9). Branched off that PR, because the specs need its harness.

This is the twin of the bug #9 fixes, eight lines into the hot path.

The bug

$('body').append('<div class="block_timerange">…</div>').ready(function () {
  // every preset handler is bound in here
});

On jQuery 1 and 2 .ready() on an already-ready document ran synchronously via .done(). jQuery 3 rebuilt it on a Deferred (jQuery.fn.ready = function (fn) { readyList.then(fn) }), and Deferred.then schedules through window.setTimeout. So:

  • the popup is on screen but inert for a macrotask after every open;
  • two quick clicks resolve both deferred callbacks against the single surviving popup and double-bind every handler;
  • .ready() on a non-document collection is deprecated in jQuery 3 and removed in jQuery 4 — there the popup would never bind at all, which is exactly the failure mode Add a dummy app, specs and CI; fix form auto-init on jQuery 3 #9 exists to fix.

The fix

Nothing here waits for anything: append is synchronous and the nodes exist on the next line. Bind straight away.

The spec

Clicks "Set range" and a preset inside a single execute_script, so both land in the same tick. On the parent commit the preset click hits an unbound popup and the input stays empty.

@Fivell
Fivell force-pushed the fix/bind-popup-synchronously branch 2 times, most recently from 172fb59 to 11c124e Compare October 1, 2026 09:27
Base automatically changed from feature/dummy-app-and-ci to master October 1, 2026 09:39
@Fivell
Fivell force-pushed the fix/bind-popup-synchronously branch 2 times, most recently from 122fe83 to 837abea Compare October 1, 2026 10:01
@Fivell
Fivell requested a balanced review from Copilot October 1, 2026 10:03

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

The focused implementation correctly removes deferred binding and includes regression coverage for both affected interactions.

Review effort: Balanced
Findings: None

What changed in this PR

Binds popup handlers synchronously, preventing inert or duplicate-bound preset popups across jQuery versions.

Changes:

  • Appends the popup and immediately registers its handlers.
  • Adds regression tests for same-tick preset selection and dismissal.
File Description
vendor/​assets/​javascripts/​active_admin_date_range_preset.js Replaces deferred .ready() binding with immediate binding.
spec/​popup_binding_spec.rb Tests synchronous popup interactions.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@Fivell
Fivell force-pushed the fix/bind-popup-synchronously branch from 837abea to 53765de Compare October 1, 2026 10:26
Fivell added 2 commits October 1, 2026 12:32
The preset handlers and the dismissal listener are attached inside
`$('body').append(...).ready(fn)`. jQuery 3 rebuilt .ready() on a Deferred,
whose .then schedules through setTimeout, so on a non-document collection
the callback lands a macrotask late and the popup is on screen but unbound
until then.

Both examples drive their clicks from a single execute_script, so they land
in the same tick: one clicks a preset, the other clicks outside to dismiss.
The preset click hits an unbound popup and the input stays empty; the
dismissal click does nothing and the popup stays up.

This commit is expected to fail CI.
Nothing here waits for anything, so bind straight away instead of deferring
through .ready(). Keep the node returned by appendTo rather than re-querying
`.block_timerange` by class, which only ever found the right popup because
unbindClickEventBlockTimerange removes every other one first.

jQuery 4 keeps the same .ready() implementation, so it is not a version
cliff -- just a macrotask nobody needs. (An earlier draft of this commit
claimed jQuery 4 removed the method; jQuery.fn.ready is present in 4.0.0
with a byte-identical body.)
@Fivell
Fivell force-pushed the fix/bind-popup-synchronously branch from 837abea to 671850c Compare October 1, 2026 10:34
@Fivell
Fivell merged commit 8c7537d into master Oct 1, 2026
15 checks passed
@Fivell
Fivell deleted the fix/bind-popup-synchronously branch October 1, 2026 10:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants