fix(collab-web): guard composer submit against IME composition
Both onKeyDown handlers in Composer.tsx committed on Enter without checking IME composition state, so confirming a Korean/Japanese/Chinese composition with Enter re-committed the composing character (한글 -> 한글글). Extract a shouldSubmitOnEnter predicate that returns false while nativeEvent.isComposing or a composing ref is set, and a useCompositionGuard hook that tracks compositionstart/compositionend with a deferred reset for the WebKit case where the confirming Enter keydown fires after compositionend. Wire both AskEditor and Composer to it. Fixes #6163
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
import { describe, expect, it } from "bun:test";
|
||||
import type { KeyboardEvent } from "react";
|
||||
import { renderToStaticMarkup } from "react-dom/server";
|
||||
import type { GuestSnapshot } from "../src/lib/client";
|
||||
import { GuestClient } from "../src/lib/client";
|
||||
import { Composer } from "../src/components/shell/Composer";
|
||||
import { Composer, shouldSubmitOnEnter } from "../src/components/shell/Composer";
|
||||
import { encodeBase64Url } from "../src/lib/link";
|
||||
|
||||
const LINK = `roomroomroom1234#${encodeBase64Url(new Uint8Array(32))}`;
|
||||
@@ -77,3 +78,35 @@ describe("Composer host UI requests", () => {
|
||||
expect(submit.disabled).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
type KeyEvt = KeyboardEvent<HTMLTextAreaElement>;
|
||||
|
||||
function keydown(key: string, opts: { shiftKey?: boolean; isComposing?: boolean } = {}): KeyEvt {
|
||||
return {
|
||||
key,
|
||||
shiftKey: opts.shiftKey ?? false,
|
||||
nativeEvent: { isComposing: opts.isComposing ?? false },
|
||||
} as KeyEvt;
|
||||
}
|
||||
|
||||
describe("shouldSubmitOnEnter IME guard", () => {
|
||||
it("submits on a plain Enter with no composition", () => {
|
||||
expect(shouldSubmitOnEnter(keydown("Enter"), false)).toBe(true);
|
||||
});
|
||||
|
||||
it("does not submit while nativeEvent.isComposing is true", () => {
|
||||
expect(shouldSubmitOnEnter(keydown("Enter", { isComposing: true }), false)).toBe(false);
|
||||
});
|
||||
|
||||
it("does not submit while the WebKit composing ref is still set", () => {
|
||||
expect(shouldSubmitOnEnter(keydown("Enter"), true)).toBe(false);
|
||||
});
|
||||
|
||||
it("does not submit on Shift+Enter (newline)", () => {
|
||||
expect(shouldSubmitOnEnter(keydown("Enter", { shiftKey: true }), false)).toBe(false);
|
||||
});
|
||||
|
||||
it("ignores non-Enter keys", () => {
|
||||
expect(shouldSubmitOnEnter(keydown("a"), false)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user