Merge pull request #3087 from ehuss/limit-global-keypress

Fix global keypresses triggering when other elements are in focus
This commit is contained in:
Eric Huss 2026-05-04 19:03:57 +00:00 committed by GitHub
commit 9190b5d093
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 129 additions and 5 deletions

View file

@ -19,6 +19,17 @@ function playground_text(playground, hidden = true) {
} }
} }
/**
* Helper for global keypress handlers so they don't trigger when certain elements are active.
* @returns {boolean} True if the keypress handler should be skipped.
*/
function mdbook_something_else_has_focus(e) {
// Check composedPath in case the event happened from something generated
// from the shadowDOM.
const target = e.composedPath()[0] || e.target;
return /^(?:input|select|textarea)$/i.test(target.nodeName);
}
(function codeSnippets() { (function codeSnippets() {
function fetch_with_timeout(url, options, timeout = 6000) { function fetch_with_timeout(url, options, timeout = 6000) {
return Promise.race([ return Promise.race([
@ -648,12 +659,15 @@ aria-label="Show hidden lines"></button>';
(function chapterNavigation() { (function chapterNavigation() {
document.addEventListener('keydown', function(e) { document.addEventListener('keydown', function(e) {
if (e.altKey || e.ctrlKey || e.metaKey) { if (e.altKey ||
return; e.ctrlKey ||
} e.metaKey ||
if (window.search && window.search.hasFocus()) { window.search && window.search.hasFocus() ||
mdbook_something_else_has_focus(e)
) {
return; return;
} }
const html = document.querySelector('html'); const html = document.querySelector('html');
function next() { function next() {

View file

@ -357,7 +357,7 @@ window.search = window.search || {};
e.shiftKey || e.shiftKey ||
e.target.type === 'textarea' || e.target.type === 'textarea' ||
e.target.type === 'text' || e.target.type === 'text' ||
!hasFocus() && /^(?:input|select|textarea)$/i.test(e.target.nodeName) !hasFocus() && mdbook_something_else_has_focus(e)
) { ) {
return; return;
} }

View file

@ -0,0 +1,6 @@
[book]
title = "editor"
language = "en"
[output.html.playground]
editable = true

View file

@ -0,0 +1,4 @@
# Summary
- [Chapter 1](./chapter_1.md)
- [Chapter 2](./chapter_2.md)

View file

@ -0,0 +1,7 @@
# Chapter 1
```rust,editable
fn main() {
println!("Hello, world!");
}
```

View file

@ -0,0 +1 @@
# Chapter 2

View file

@ -0,0 +1,6 @@
[book]
title = "shadow-dom"
language = "en"
[output.html]
additional-js = ["shadow-dom.js"]

View file

@ -0,0 +1,38 @@
(function() {
let shadowHost = null;
let shadowInput = null;
document.addEventListener('keypress', function(e) {
if (e.key === 'x' || e.key === 'X') {
if (shadowHost && shadowHost.isConnected) {
shadowInput.focus();
return;
}
shadowHost = document.createElement('div');
shadowHost.id = 'shadow-input-host';
shadowHost.style.cssText = 'position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);z-index:9999;';
document.body.appendChild(shadowHost);
const shadowRoot = shadowHost.attachShadow({ mode: 'open' });
shadowInput = document.createElement('input');
shadowInput.type = 'text';
shadowInput.id = 'shadow-input';
shadowInput.placeholder = 'Shadow DOM input (press Escape to close)';
shadowInput.style.cssText = 'font-size:1.2em;padding:8px;width:300px;';
shadowRoot.appendChild(shadowInput);
shadowInput.focus();
shadowInput.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
shadowHost.remove();
shadowHost = null;
shadowInput = null;
}
});
}
});
})();

View file

@ -0,0 +1,3 @@
# Summary
- [Chapter 1](./chapter_1.md)

View file

@ -0,0 +1 @@
# Chapter 1

View file

@ -0,0 +1,24 @@
// Tests for global keypress handlers when ACE editor is in focus.
// See https://github.com/rust-lang/mdBook/issues/3064
go-to: |DOC_PATH| + "editor/chapter_1.html"
click: ".ace_editor"
press-key: "s"
// Wait briefly to allow any event handlers triggered by the keypress to run.
// Otherwise there is a race here since the wrapper is already display:none.
wait-for: 200
wait-for-css: ("#mdbook-search-wrapper", {"display": "none"})
// ? inside ACE editor shouldn't show global help popup.
press-key: "?"
wait-for: 200
wait-for-css: ("#mdbook-help-container", {"display": "none"})
// Make sure arrow keys don"t navigate.
press-key: "ArrowRight"
wait-for: 200
assert-window-property: ({"location": |DOC_PATH| + "editor/chapter_1.html"})
press-key: "ArrowLeft"
wait-for: 200
assert-window-property: ({"location": |DOC_PATH| + "editor/chapter_1.html"})

20
tests/gui/shadow-dom.goml Normal file
View file

@ -0,0 +1,20 @@
// Tests the keypress handler when there is a shadow-dom element.
// See https://github.com/rust-lang/mdBook/issues/2507
go-to: |DOC_PATH| + "shadow-dom/chapter_1.html"
// Open the shadow-dom generated element.
press-key: 'x'
wait-for: '#shadow-input-host'
// See what happens when the s key is pressed.
press-key: 's'
wait-for: 200
wait-for-css: ("#mdbook-search-wrapper", {"display": "none"})
// Also try the global key handlers.
reload:
press-key: 'x'
wait-for: '#shadow-input-host'
press-key: '?'
wait-for: 200
wait-for-css: ("#mdbook-help-container", {"display": "none"})