Sample report: two IME defects in open-source UI components
This is not a client report. I built it from two fixes of mine that were merged into rsuite and tdesign-vue-next, so every claim below can be checked against the merged pull request. A paid report has the same sections, with your app's inputs in place of these components.
これは顧客向けの報告書ではありません。rsuiteとtdesign-vue-nextにマージされた自分の修正2件から作ったサンプルです。実際の報告書も同じ構成で、対象がお客様のアプリの入力欄になります。
Summary
| ID | Input | Key | What goes wrong | Severity |
|---|---|---|---|---|
| F1 | rsuite InputPicker, single select | Enter | Confirming a conversion selects the highlighted option and closes the menu. | High |
| F2 | tdesign-vue-next Drawer with closeOnEscKeydown | Esc | Cancelling a candidate closes the drawer the user is typing in. | Medium |
Severity: High means a wrong value is saved or sent without the user noticing. Medium means the user loses their place or has to redo work, but no wrong data is stored.
F1. InputPicker selects an option on the Enter that confirms a conversion
Where
src/InputPicker/InputPicker.tsx, handleMenuItemKeyPress. The tag-input handler a few lines above (handleTagKeyPress) already ignores Enter during composition; the single-select handler did not.
What happens
The user filters the list in Japanese. The Enter meant to confirm the kanji also fires onChange with whichever option is focused, and the menu closes. The value saved is an option the user never chose.
Steps to reproduce
- Render a single-select
InputPickerand open it. - Turn on a Japanese IME and type in the search box to filter the list. An option becomes focused.
- Press Enter to confirm the conversion.
Expected: the text is confirmed and the picker stays open. Actual: onChange fires, the focused option is selected, and the menu closes.
Cause
The handler reacts to every Enter keydown and never reads isComposing. During composition the browser still reports the key as Enter.
Patch
const handleMenuItemKeyPress = useEventCallback((event: React.KeyboardEvent) => {
+ // When composing, ignore the keypress event.
+ if (event.nativeEvent.isComposing) {
+ return;
+ }
+ if (!focusItemValue || !controlledData) {
return;
}
Full diff: rsuite/rsuite #4585, files changed. The guard reads nativeEvent because React's synthetic keyboard event has no isComposing.
Regression test
it('Should not call `onChange` when Enter is pressed during IME composition', () => {
const onChange = vi.fn();
render(<InputPicker defaultOpen data={data} onChange={onChange} defaultValue={'Kariane'} />);
fireEvent.keyDown(screen.getByRole('textbox'), { key: 'Enter', isComposing: true });
expect(onChange).not.toHaveBeenCalled();
});
Without the guard the test fails (onChange is called once); with it, the full InputPicker suite passes.
Status
Merged upstream on 2 July 2026.
F2. Esc that cancels a candidate also closes the drawer
Where
packages/components/drawer/drawer.tsx, handleEscKeydown. The same project had fixed this for Dialog in #6740; Drawer was missing the guard.
What happens
With closeOnEscKeydown on, the user is typing Japanese in a field inside the drawer and presses Esc to back out of a conversion. The drawer closes. With destroyOnClose, whatever they typed in the drawer is gone.
Steps to reproduce
- Open a
t-drawerwithcloseOnEscKeydownand a text input inside. - Turn on a Japanese IME, type in the input, and convert so a candidate is showing.
- Press Esc to cancel the candidate.
Expected: only the candidate is cancelled. Actual: the drawer closes.
Cause
The drawer listens for keydown on the window and checks only e.key === 'Escape'. An Escape pressed during composition belongs to the IME.
Patch
const handleEscKeydown = (e: KeyboardEvent) => {
+ if (e.key === 'Escape' && e.isComposing) return; if (
(props.closeOnEscKeydown ?? globalConfig.value.closeOnEscKeydown) &&
e.key === 'Escape' &&
Full diff: Tencent/tdesign-vue-next #6756, files changed. The merged line carries a one-line comment in Chinese, omitted here.
Regression test
it('should not close on ESC during IME composition', async () => {
const onClose = vi.fn();
const wrapper = mount(Drawer, {
attachTo: document.body,
props: { visible: true, body: '内容', closeOnEscKeydown: true, onClose },
});
vi.runAllTimers();
await nextTick();
window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape', isComposing: true }));
await nextTick();
expect(onClose).not.toHaveBeenCalled();
// A normal ESC still closes the drawer.
window.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
await nextTick();
expect(onClose).toHaveBeenCalledTimes(1);
expect(onClose.mock.calls[0][0].trigger).toBe('esc');
wrapper.unmount();
});
The test checks both sides: Escape during composition leaves the drawer open, and a normal Escape still closes it.
Status
Merged upstream on 30 June 2026.
Inputs that passed
A paid report also lists every input that passed, with the browsers and key sequences tried, so you know what was covered. This sample has none, because it covers only the two components above.