Skip to content

Commit 7dbadeb

Browse files
committed
fix: make key filters work on non-latin layouts
Key filters matched event.key only, so keydown.j failed on non-latin layouts. Fallback to event.code for non-ASCII keys and add regression tests.
1 parent 422eb81 commit 7dbadeb

File tree

2 files changed

+62
-1
lines changed

2 files changed

+62
-1
lines changed

src/core/action.ts

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,10 @@ export class Action {
5959
error(`contains unknown key filter: ${this.keyFilter}`)
6060
}
6161

62-
return this.keyMappings[standardFilter].toLowerCase() !== event.key.toLowerCase()
62+
const mappedKey = this.keyMappings[standardFilter].toLowerCase()
63+
const normalizedEventKey = this.normalizedKeyboardEventKey(event, mappedKey)
64+
65+
return mappedKey !== normalizedEventKey
6366
}
6467

6568
shouldIgnoreMouseEvent(event: MouseEvent): boolean {
@@ -102,6 +105,17 @@ export class Action {
102105

103106
return event.metaKey !== meta || event.ctrlKey !== ctrl || event.altKey !== alt || event.shiftKey !== shift
104107
}
108+
109+
private normalizedKeyboardEventKey(event: KeyboardEvent, mappedKey: string): string {
110+
const eventKey = event.key.toLowerCase()
111+
112+
if (!shouldUseCodeFallback(event, mappedKey)) {
113+
return eventKey
114+
}
115+
116+
const keyFromCode = keyboardEventKeyFromCode(event.code)
117+
return keyFromCode || eventKey
118+
}
105119
}
106120

107121
const defaultEventNames: { [tagName: string]: (element: Element) => string } = {
@@ -132,3 +146,25 @@ function typecast(value: any): any {
132146
return value
133147
}
134148
}
149+
150+
function shouldUseCodeFallback(event: KeyboardEvent, mappedKey: string): boolean {
151+
return !event.isComposing && isPrintableASCIICharacter(mappedKey) && !isPrintableASCIICharacter(event.key)
152+
}
153+
154+
function isPrintableASCIICharacter(value: string): boolean {
155+
return value.length === 1 && value >= " " && value <= "~"
156+
}
157+
158+
function keyboardEventKeyFromCode(code: string): string | null {
159+
const keyMatch = code.match(/^Key([A-Z])$/)
160+
if (keyMatch) {
161+
return keyMatch[1].toLowerCase()
162+
}
163+
164+
const digitMatch = code.match(/^Digit([0-9])$/)
165+
if (digitMatch) {
166+
return digitMatch[1]
167+
}
168+
169+
return null
170+
}

src/tests/modules/core/action_keyboard_filter_tests.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export default class ActionKeyboardFilterTests extends LogControllerTestCase {
2222
<button id="button8" data-action="keydown.a->a#log keydown.b->a#log2"></button>
2323
<button id="button9" data-action="keydown.shift+a->a#log keydown.a->a#log2 keydown.ctrl+shift+a->a#log3">
2424
<button id="button10" data-action="jquery.custom.event->a#log jquery.a->a#log2">
25+
<button id="button11" data-action="keydown.j->a#log"></button>
2526
</div>
2627
`
2728

@@ -197,4 +198,28 @@ export default class ActionKeyboardFilterTests extends LogControllerTestCase {
197198
await this.triggerEvent(button, "jquery.a")
198199
this.assertActions({ name: "log2", identifier: "a", eventType: "jquery.a", currentTarget: button })
199200
}
201+
202+
// Cyrillic (Bulgarian, phonetic layout)
203+
async "test plain j key filter falls back to code for cyrillic key"() {
204+
const button = this.findElement("#button11")
205+
await this.nextFrame
206+
await this.triggerKeyboardEvent(button, "keydown", { key: "й", code: "KeyJ" })
207+
this.assertActions({ name: "log", identifier: "a", eventType: "keydown", currentTarget: button })
208+
}
209+
210+
// Greek
211+
async "test plain j key filter falls back to code for greek key"() {
212+
const button = this.findElement("#button11")
213+
await this.nextFrame
214+
await this.triggerKeyboardEvent(button, "keydown", { key: "ξ", code: "KeyJ" })
215+
this.assertActions({ name: "log", identifier: "a", eventType: "keydown", currentTarget: button })
216+
}
217+
218+
// Hebrew
219+
async "test plain j key filter falls back to code for hebrew key"() {
220+
const button = this.findElement("#button11")
221+
await this.nextFrame
222+
await this.triggerKeyboardEvent(button, "keydown", { key: "ח", code: "KeyJ" })
223+
this.assertActions({ name: "log", identifier: "a", eventType: "keydown", currentTarget: button })
224+
}
200225
}

0 commit comments

Comments
 (0)