diff --git a/crates/mdbook-html/front-end/js/book.js b/crates/mdbook-html/front-end/js/book.js index 62d7c4cc..fe74b710 100644 --- a/crates/mdbook-html/front-end/js/book.js +++ b/crates/mdbook-html/front-end/js/book.js @@ -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 fetch_with_timeout(url, options, timeout = 6000) { return Promise.race([ @@ -648,12 +659,15 @@ aria-label="Show hidden lines">'; (function chapterNavigation() { document.addEventListener('keydown', function(e) { - if (e.altKey || e.ctrlKey || e.metaKey) { - return; - } - if (window.search && window.search.hasFocus()) { + if (e.altKey || + e.ctrlKey || + e.metaKey || + window.search && window.search.hasFocus() || + mdbook_something_else_has_focus(e) + ) { return; } + const html = document.querySelector('html'); function next() { diff --git a/crates/mdbook-html/front-end/searcher/searcher.js b/crates/mdbook-html/front-end/searcher/searcher.js index 5c4bab65..ea17ae38 100644 --- a/crates/mdbook-html/front-end/searcher/searcher.js +++ b/crates/mdbook-html/front-end/searcher/searcher.js @@ -357,7 +357,7 @@ window.search = window.search || {}; e.shiftKey || e.target.type === 'textarea' || e.target.type === 'text' || - !hasFocus() && /^(?:input|select|textarea)$/i.test(e.target.nodeName) + !hasFocus() && mdbook_something_else_has_focus(e) ) { return; } diff --git a/tests/gui/books/editor/book.toml b/tests/gui/books/editor/book.toml new file mode 100644 index 00000000..2af92d4c --- /dev/null +++ b/tests/gui/books/editor/book.toml @@ -0,0 +1,6 @@ +[book] +title = "editor" +language = "en" + +[output.html.playground] +editable = true diff --git a/tests/gui/books/editor/src/SUMMARY.md b/tests/gui/books/editor/src/SUMMARY.md new file mode 100644 index 00000000..fa67cf3e --- /dev/null +++ b/tests/gui/books/editor/src/SUMMARY.md @@ -0,0 +1,4 @@ +# Summary + +- [Chapter 1](./chapter_1.md) +- [Chapter 2](./chapter_2.md) diff --git a/tests/gui/books/editor/src/chapter_1.md b/tests/gui/books/editor/src/chapter_1.md new file mode 100644 index 00000000..8982f7d3 --- /dev/null +++ b/tests/gui/books/editor/src/chapter_1.md @@ -0,0 +1,7 @@ +# Chapter 1 + +```rust,editable +fn main() { + println!("Hello, world!"); +} +``` diff --git a/tests/gui/books/editor/src/chapter_2.md b/tests/gui/books/editor/src/chapter_2.md new file mode 100644 index 00000000..7ebb5961 --- /dev/null +++ b/tests/gui/books/editor/src/chapter_2.md @@ -0,0 +1 @@ +# Chapter 2 diff --git a/tests/gui/books/shadow-dom/book.toml b/tests/gui/books/shadow-dom/book.toml new file mode 100644 index 00000000..edba3344 --- /dev/null +++ b/tests/gui/books/shadow-dom/book.toml @@ -0,0 +1,6 @@ +[book] +title = "shadow-dom" +language = "en" + +[output.html] +additional-js = ["shadow-dom.js"] diff --git a/tests/gui/books/shadow-dom/shadow-dom.js b/tests/gui/books/shadow-dom/shadow-dom.js new file mode 100644 index 00000000..950442e2 --- /dev/null +++ b/tests/gui/books/shadow-dom/shadow-dom.js @@ -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; + } + }); + } + }); +})(); diff --git a/tests/gui/books/shadow-dom/src/SUMMARY.md b/tests/gui/books/shadow-dom/src/SUMMARY.md new file mode 100644 index 00000000..7390c828 --- /dev/null +++ b/tests/gui/books/shadow-dom/src/SUMMARY.md @@ -0,0 +1,3 @@ +# Summary + +- [Chapter 1](./chapter_1.md) diff --git a/tests/gui/books/shadow-dom/src/chapter_1.md b/tests/gui/books/shadow-dom/src/chapter_1.md new file mode 100644 index 00000000..b743fda3 --- /dev/null +++ b/tests/gui/books/shadow-dom/src/chapter_1.md @@ -0,0 +1 @@ +# Chapter 1 diff --git a/tests/gui/editor-keypress.goml b/tests/gui/editor-keypress.goml new file mode 100644 index 00000000..f2cfe6f2 --- /dev/null +++ b/tests/gui/editor-keypress.goml @@ -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"}) diff --git a/tests/gui/shadow-dom.goml b/tests/gui/shadow-dom.goml new file mode 100644 index 00000000..2b401f20 --- /dev/null +++ b/tests/gui/shadow-dom.goml @@ -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"})