Repository navigation
fix(form): keep Toggle, Checkbox and RadioGroup clickable inside Sheet - #3
Merged
Merged
Conversation
…e, Checkbox and RadioGroup Switch.Root, Checkbox.Root and Radio.Root render as <span role="..."> rather than a native control. Base UI's swipe-to-dismiss gesture (Sheet/Dialog) only skips button,a,input,select,textarea,label, [role="button"] by default (useSwipeDismiss.mjs), so a mouse press on one of these controls inside an open Sheet started the drawer's swipe gesture instead, which calls setPointerCapture on the popup (DrawerViewport.mjs) and retargets the click away -- onCheckedChange never fired, with no error. jsdom has no pointer capture, so this never reproduced in the existing unit tests. Each affected root now sets data-base-ui-swipe-ignore, Base UI's own opt-out for this. Adds a unit test asserting the attribute is present, and a Playwright test that clicks a Toggle inside an open Sheet and asserts onCheckedChange fired -- verified it fails without the fix and passes with it. Claude-Session: https://claude.ai/code/session_01MThGUPSpK3FBPz62WLoVuW
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.
Symptom
ToggleandCheckboxplaced inside aSheet(a Base UIDrawerwithswipe-to-dismiss) silently ignore a mouse click.
onCheckedChangeneverfires and nothing errors.
RadioGrouphas the same problem.Root cause
Switch.Root,Checkbox.RootandRadio.Rootrender as<span role="switch"|"checkbox"|"radio">, not a native form element.Base UI's swipe-to-dismiss gesture only treats
button,a,input,select,textarea,label,[role="button"]as interactive bydefault (
@base-ui/react/utils/useSwipeDismiss.mjs,DEFAULT_IGNORE_SELECTORat line 16). Everything else is fair game for the drag gesture.
A mouse press on one of these spans inside an open
Sheettherefore startsthe drawer's swipe gesture instead of the control's own click handling.
DrawerViewport.mjs's pointerdown handling callssetPointerCaptureon thepopup element (around line 383 of
useSwipeDismiss.mjs, gated byisSwipeIgnoredTargetinDrawerViewport.mjs), which retargets theeventual click away from the control before
onCheckedChangeever runs.jsdom has no pointer capture implementation, so none of the existing unit
tests reproduce this -- it only shows up in a real browser.
Base UI's own opt-out is the
data-base-ui-swipe-ignoreattribute(
internals/constants.mjs; a legacydata-swipe-ignorealso exists), whichisSwipeIgnoredTargetchecks viatarget.closest(...).Fix
Toggle.tsx:data-base-ui-swipe-ignoreonSwitch.Root.Checkbox.tsx:data-base-ui-swipe-ignoreonCheckbox.Root.RadioGroup.tsx:data-base-ui-swipe-ignoreon eachRadio.Rootoption.Checked the rest of the package for other non-native role-span controls that
could sit in a
Sheet:Select.TriggerandCombobox.Inputrender a real<button>/<input>(already covered by Base UI's default ignore list), andMenu'sCheckboxItem/RadioItempopups portal to<body>, outside theSheet's own DOM subtree, so they're never a descendant the swipe gesture can
capture regardless of role. No
SliderorTabscomponent exists in thispackage yet.
Each attribute is added as one of the component's own controlled props,
spread after
...rest, keeping the existing rule (seeToggle.tsx's doccomment): rest props are spread before the component's own controlled props,
so a caller can't silently clobber them.
Tests
src/form/swipeIgnore.test.tsx(unit, jsdom-safe): asserts the attributerenders on Toggle's root, Checkbox's root, and every RadioGroup option.
playground/tests/sheet.spec.ts: a new Playwright test opens the rightSheet (now carrying a
Togglein its body), clicks it, and assertsonCheckedChangefired andaria-checkedflipped. Verified this testfails without the fix (the toggle stays unchecked, the counter stays at
0) and passes with it.No changelog file exists in this repo, and past fix PRs (
4e25deb,c08a19b,60accca) didn't bumppackage.json-- version bumps happen inseparate
release: x.y.zcommits -- so this PR doesn't touch the version.playground/package-lock.jsonpicks up an unrelated pre-existing drift(root
package.jsonwas already at0.3.1, the lockfile still said0.3.0), refreshed in its own commit matching the repo's existingchore(playground): refresh the lockfilepattern.Test plan
npm run lintnpm run typechecknpm run buildnpm test(vitest, includes the new unit test)cd playground && npx playwright test tests/sheet.spec.ts tests/choice.spec.ts(36 passed, includes the new browser test)https://claude.ai/code/session_01MThGUPSpK3FBPz62WLoVuW