Repository navigation
OTP input: don't fire input or complete events when Delete or Backspace has nothing to remove - #43255
Merged
Merged
Conversation
…ce has nothing to remove Co-Authored-By: Claude Opus 5.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.
Since #43252, a Delete with nothing after the caret still fires
input.bs.otpInput, and on a full code it firescomplete.bs.otpInputagain. In Chromium, End then Delete on a full code triggers a secondcomplete, so a form that submits oncompletesubmits twice. The handler cancels the browser's action, finds nothing to remove, and still runs_afterValueChange(). Now it returns before that when nothing changes.The same check covers Backspace with nothing before the caret. In an empty field it has fired an
inputevent with an empty value since the OTP input was added, and it no longer does. Both cases now behave as a plain input, which fires nothing when there's nothing to delete.Follow-up to #43252.
Notes: what was checked, and how to check it again
Measured
Real mouse clicks and key presses with Playwright 1.64.1 in Chromium 156, Firefox 157 and WebKit 27.2, on the bundles built from
mainbefore #43252 (f0dcf4b), frommaintoday (a4d4afa) and from this branch. Each cell is the number ofinput/completeevents the key fired.main123456, End then Delete1256, click the first empty slot, DeleteIn Firefox and WebKit, End selects the last slot, so Delete removes it and fires one
inputevent on all three builds.Impact
The change touches only the delete branch of the OTP input's
beforeinputhandler. I ran the same sequence on every example of the OTP docs page: default, connected, four-digit PIN, groups (data-bs-groups="[3,3]"),.otp-sm,.otp-lg, alphanumeric, masked,.is-invalidand with a form label. The sequence presses Backspace in the empty field, types a full code, then presses End and Delete. Then it types 9 over the third slot, presses Backspace and Delete on a selected slot, Delete on the first empty slot, and ←. It records the value, the active slot and the events after each key.Typing, overwriting, and Backspace or Delete on a filled slot give the same value, active slot and events on all three builds, in all three engines. Only the cases in the table change. The readonly, disabled and
<fieldset disabled>fields fire no event onmainor with this PR.Checks
npm run js-test-unit: 1,373 tests pass. Two of the three new event tests fail onmain(Delete after the last character, Backspace before the first one). The third, oneinputevent when Delete removes a character, passes on both and guards the normal case.npm run js-lint: no errors. Its six warnings are in other files and also show onmain.npm run js-typecheckpasses.bootstrap.bundle.min.jsstays at 58.54 kB (budget 59 kB),bootstrap.min.jsgoes from 34.77 kB to 34.76 kB (budget 35 kB).How to check it again
Build the bundle on each side with
npm run js-compile-bundle, copydist/js/bootstrap.bundle.jsto.probe/before.js(at f0dcf4b),.probe/main.js(at a4d4afa) and.probe/fix.js(on this branch), and revertdist/. Save the page as.probe/index.html, serve the repository root withnpx http-server -p 47412 -s -c-1 ., and runnode probe.mjs http://127.0.0.1:47412/.probe/index.htmlfrom the repository root.🤖 Generated with Claude Code