Bug: the standalone Bottom Sheet dismisses on Escape without guarding against IME composition, so a CJK (Korean/Japanese/Chinese) user pressing Escape to cancel an in-progress composition dismisses the sheet instead.
When an IME is composing, the browser fires a keydown for Escape (cancel) / Enter (commit) before compositionend. A handler that reads a bare event.key === 'Escape' misreads the composition-cancel as an app command. utils/ime.ts names closing a dialog mid-composition as exactly this bug, and the fix is isImeKeyEvent(event.nativeEvent) early-return.
The base Dialog guards it (Dialog.tsx, in its Escape keydown handler) and so does the sibling BottomSheetSwitcher (its handleKeyDown calls isImeKeyEvent(event.nativeEvent), with a dedicated test). The standalone BottomSheet does not — neither its handleKeyDown nor its native onCancel (handleCancel) guards.
Where: packages/core/src/BottomSheet/BottomSheet.tsx — handleKeyDown and handleCancel in StandaloneBottomSheet.
Reachable path: the documented purpose="form" and mobile-keyboard (height="tall") use cases put a text field in a standalone sheet. A CJK user composing in that field who presses Escape to cancel the candidate loses the whole sheet.
Why lint doesn't catch it: @astryx/no-unguarded-ime-keydown only flags onKeyDown on an editable surface (input/textarea/contentEditable/role=textbox). The handler here is on the <dialog>, which the rule's heuristic treats as non-editable, so this is a manual finding.
Fix: early-return on isImeKeyEvent(event.nativeEvent) at the top of both handleKeyDown and handleCancel, mirroring Dialog, and add a standalone IME-Escape test (the switcher already has one). The switcher path is already correct.
Filed from a full Component Audit Rubric v1.6 grading of BottomSheet (mode O). Rule: §9 I19 (IME composition guard). Component ledger entry: core/BottomSheet — 89.1 / C (this is the one open BLOCK; the C cap lifts and the projection is A once it's fixed). Rubric: https://github.com/facebook/astryx/wiki/Component-Audit-Rubric
Bug: the standalone Bottom Sheet dismisses on Escape without guarding against IME composition, so a CJK (Korean/Japanese/Chinese) user pressing Escape to cancel an in-progress composition dismisses the sheet instead.
When an IME is composing, the browser fires a
keydownfor Escape (cancel) / Enter (commit) beforecompositionend. A handler that reads a bareevent.key === 'Escape'misreads the composition-cancel as an app command.utils/ime.tsnames closing a dialog mid-composition as exactly this bug, and the fix isisImeKeyEvent(event.nativeEvent)early-return.The base
Dialogguards it (Dialog.tsx, in its Escapekeydownhandler) and so does the siblingBottomSheetSwitcher(itshandleKeyDowncallsisImeKeyEvent(event.nativeEvent), with a dedicated test). The standaloneBottomSheetdoes not — neither itshandleKeyDownnor its nativeonCancel(handleCancel) guards.Where:
packages/core/src/BottomSheet/BottomSheet.tsx—handleKeyDownandhandleCancelinStandaloneBottomSheet.Reachable path: the documented
purpose="form"and mobile-keyboard (height="tall") use cases put a text field in a standalone sheet. A CJK user composing in that field who presses Escape to cancel the candidate loses the whole sheet.Why lint doesn't catch it:
@astryx/no-unguarded-ime-keydownonly flagsonKeyDownon an editable surface (input/textarea/contentEditable/role=textbox). The handler here is on the<dialog>, which the rule's heuristic treats as non-editable, so this is a manual finding.Fix: early-return on
isImeKeyEvent(event.nativeEvent)at the top of bothhandleKeyDownandhandleCancel, mirroringDialog, and add a standalone IME-Escape test (the switcher already has one). The switcher path is already correct.Filed from a full Component Audit Rubric v1.6 grading of BottomSheet (mode O). Rule: §9 I19 (IME composition guard). Component ledger entry:
core/BottomSheet— 89.1 / C (this is the one open BLOCK; the C cap lifts and the projection is A once it's fixed). Rubric: https://github.com/facebook/astryx/wiki/Component-Audit-Rubric