Conversation
…nd tests covering inputs, contenteditable and non-editable cases
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #7510 +/- ##
=======================================
Coverage 99.61% 99.61%
=======================================
Files 283 283
Lines 11877 11895 +18
Branches 2898 2905 +7
=======================================
+ Hits 11831 11849 +18
Misses 46 46 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
…arget.selectionStart
There was a problem hiding this comment.
Pull request overview
This PR updates the sidebar’s search-related keyboard shortcuts so they don’t trigger while the user is typing in editable elements, by leveraging a shared ignoreWhenEditable shortcut option and expanding test coverage to prevent regressions.
Changes:
- Add
ignoreWhenEditablesupport toinstallShortcut/useShortcutand cover it with new unit tests. - Update sidebar components to set
ignoreWhenEditable: truefor search/apply-updates shortcuts. - Expand
SearchIconButtontests to ensure shortcuts don’t fire in inputs and don’t re-open an already-open panel.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/shared/shortcut.ts | Adds ignoreWhenEditable option and editable-target detection for shortcut suppression. |
| src/shared/test/shortcut-test.js | Adds tests verifying ignoreWhenEditable behavior. |
| src/sidebar/components/search/SearchIconButton.tsx | Enables ignoreWhenEditable for / and Ctrl/Cmd+K search shortcuts. |
| src/sidebar/components/search/SearchField.tsx | Enables ignoreWhenEditable for “open search” focus behavior. |
| src/sidebar/components/PendingUpdatesNotification.tsx | Prevents apply-updates shortcut from firing while typing. |
| src/sidebar/components/search/test/SearchIconButton-test.js | Updates and adds tests for the new shortcut suppression behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (target instanceof HTMLTextAreaElement) { | ||
| return true; | ||
| } | ||
| if (target instanceof HTMLInputElement) { | ||
| try { | ||
| return target.selectionStart !== null; | ||
| } catch { | ||
| return false; | ||
| } | ||
| } |
There was a problem hiding this comment.
isEditableTarget is less comprehensive than the existing isEditableContext helper used elsewhere to avoid intercepting shortcuts in editable contexts (eg. it doesn’t account for role="textbox"|"searchbox"|"combobox", and it treats some editable <input> types as non-editable). This can cause ignoreWhenEditable shortcuts to still fire while users are typing in ARIA textboxes/comboboxes or certain input types, which undermines the goal of disabling shortcuts while typing (WCAG 2.1.4). Consider reusing isEditableContext (eg. moving it into a shared module) or matching its logic here, and add/update tests to cover role-based editable targets.
| if (target instanceof HTMLTextAreaElement) { | |
| return true; | |
| } | |
| if (target instanceof HTMLInputElement) { | |
| try { | |
| return target.selectionStart !== null; | |
| } catch { | |
| return false; | |
| } | |
| } | |
| // Treat ARIA text input widgets as editable. | |
| const role = target.getAttribute('role'); | |
| if (role === 'textbox' || role === 'searchbox' || role === 'combobox') { | |
| return true; | |
| } | |
| if (target instanceof HTMLTextAreaElement) { | |
| return true; | |
| } | |
| if (target instanceof HTMLInputElement) { | |
| // Consider common text-like input types as editable. If no type is set, | |
| // it defaults to "text" in HTML. | |
| const type = (target.type || '').toLowerCase(); | |
| const editableInputTypes = [ | |
| '', | |
| 'text', | |
| 'search', | |
| 'email', | |
| 'url', | |
| 'tel', | |
| 'password', | |
| ]; | |
| return editableInputTypes.includes(type); | |
| } |
|
|
||
| /** | ||
| * Install a shortcut key listener on the document. | ||
| *Install a shortcut key listener on the document. |
There was a problem hiding this comment.
JSDoc formatting regression: there is a missing space after the leading * in this comment (*Install...), which will render incorrectly in generated docs and makes the block inconsistent with surrounding JSDoc style.
| *Install a shortcut key listener on the document. | |
| * Install a shortcut key listener on the document. |
| const modifierKey = isMacOS() ? 'meta' : 'ctrl'; | ||
|
|
||
| useShortcut(shortcuts.openSearch, openSearch); | ||
| useShortcut(`${modifierKey}+k`, openSearch); | ||
| useShortcut(shortcuts.openSearch, openSearch, { ignoreWhenEditable: true }); | ||
| useShortcut(`${modifierKey}+k`, openSearch, { ignoreWhenEditable: true }); |
There was a problem hiding this comment.
With ignoreWhenEditable: true now enabled for both shortcuts, the inline comment and logic inside openSearch that describes allowing Ctrl/Cmd+K while focused in inputs is no longer accurate. Either update the comment to reflect the new behavior (no shortcuts while typing) and/or simplify the now-redundant input/textarea guard if it’s no longer needed.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| function isEditableTarget(target: EventTarget | null): boolean { | ||
| if (!(target instanceof HTMLElement)) { | ||
| return false; | ||
| } | ||
| if (target.isContentEditable) { | ||
| return true; | ||
| } | ||
| if (target instanceof HTMLTextAreaElement) { | ||
| return true; | ||
| } | ||
| if (target instanceof HTMLInputElement) { | ||
| try { | ||
| return target.selectionStart !== null; | ||
| } catch { | ||
| return false; | ||
| } | ||
| } | ||
| return false; | ||
| } |
There was a problem hiding this comment.
isEditableTarget does not treat elements with ARIA roles like role="textbox"/searchbox/combobox (or descendants) as editable. Elsewhere in the codebase, keyboard shortcut interception is disabled in these role-based editable contexts for WCAG 2.1.4. Consider extending isEditableTarget (or reusing/moving the existing isEditableContext helper) so ignoreWhenEditable also skips shortcuts while focus is inside role-based editable widgets (eg. custom editors).
| assert.notCalled(onPress); | ||
| document.body.removeChild(div); | ||
| }); | ||
|
|
There was a problem hiding this comment.
If ignoreWhenEditable is intended to cover all editable contexts (including textarea and role-based editable widgets), the new tests only cover <input> and contenteditable. Consider adding coverage for <textarea> and an element with role="textbox" (and/or a descendant of one) so regressions in the editable-target detection don’t slip in.
| it('does not trigger when target is a textarea if ignoreWhenEditable is set', () => { | |
| const onPress = sinon.stub(); | |
| const textarea = document.createElement('textarea'); | |
| document.body.appendChild(textarea); | |
| const removeShortcut = installShortcut('a', onPress, { | |
| ignoreWhenEditable: true, | |
| }); | |
| const event = new KeyboardEvent('keydown', { key: 'a', bubbles: true }); | |
| textarea.dispatchEvent(event); | |
| removeShortcut(); | |
| assert.notCalled(onPress); | |
| document.body.removeChild(textarea); | |
| }); | |
| it('does not trigger when target has role="textbox" if ignoreWhenEditable is set', () => { | |
| const onPress = sinon.stub(); | |
| const div = document.createElement('div'); | |
| div.setAttribute('role', 'textbox'); | |
| document.body.appendChild(div); | |
| const removeShortcut = installShortcut('a', onPress, { | |
| ignoreWhenEditable: true, | |
| }); | |
| const event = new KeyboardEvent('keydown', { key: 'a', bubbles: true }); | |
| div.dispatchEvent(event); | |
| removeShortcut(); | |
| assert.notCalled(onPress); | |
| document.body.removeChild(div); | |
| }); |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @@ -70,10 +70,38 @@ export type ShortcutOptions = { | |||
| * `document.body`. | |||
There was a problem hiding this comment.
ShortcutOptions.rootElement docs say the listener defaults to document.body, but installShortcut actually defaults to document.documentElement. Please update the doc comment to match the actual default to avoid misleading API consumers.
| * `document.body`. | |
| * `document.documentElement`. |
| target.closest('[role="textbox"], [role="searchbox"]') instanceof | ||
| HTMLElement |
There was a problem hiding this comment.
isEditableTarget doesn’t treat [role="combobox"] as editable, but elsewhere in the codebase isEditableContext includes it for shortcut suppression (WCAG 2.1.4). Add combobox to this role check (or reuse the existing isEditableContext helper) so shortcuts are also ignored while interacting with combobox widgets.
| target.closest('[role="textbox"], [role="searchbox"]') instanceof | |
| HTMLElement | |
| target.closest( | |
| '[role="textbox"], [role="searchbox"], [role="combobox"]', | |
| ) instanceof HTMLElement |
| try { | ||
| return target.selectionStart !== null; | ||
| } catch { | ||
| return false; | ||
| } |
There was a problem hiding this comment.
In isEditableTarget, the HTMLInputElement branch treats inputs as editable only if selectionStart !== null, and returns false if reading selectionStart throws. This means ignoreWhenEditable won’t suppress shortcuts for some editable input types (eg type=number where selectionStart can throw). Consider treating all <input> elements as editable (or at least treating the exception case as editable) to match the intended semantics of ignoreWhenEditable.
| try { | |
| return target.selectionStart !== null; | |
| } catch { | |
| return false; | |
| } | |
| return true; |
|
|
||
| /** | ||
| * Install a shortcut key listener on the document. | ||
| *Install a shortcut key listener on the document. |
There was a problem hiding this comment.
JSDoc formatting: there’s a missing space after the leading * in this comment (*Install...), which breaks standard JSDoc formatting and may affect generated docs.
| *Install a shortcut key listener on the document. | |
| * Install a shortcut key listener on the document. |
| it('fires shortcut when input selectionStart cannot be read', () => { | ||
| const onPress = sinon.stub(); | ||
| const removeShortcut = installShortcut('a', onPress, { | ||
| ignoreWhenEditable: true, | ||
| }); | ||
|
|
||
| const input = document.createElement('input'); | ||
| Object.defineProperty(input, 'selectionStart', { | ||
| get() { | ||
| throw new Error('no selectionStart'); | ||
| }, | ||
| }); | ||
| const event = new KeyboardEvent('keydown', { key: 'a', bubbles: true }); | ||
| Object.defineProperty(event, 'target', { value: input }); | ||
|
|
||
| document.documentElement.dispatchEvent(event); | ||
| removeShortcut(); | ||
|
|
||
| assert.called(onPress); | ||
| }); |
There was a problem hiding this comment.
This test asserts that shortcuts still fire when selectionStart can’t be read, which contradicts the goal of ignoreWhenEditable (don’t fire while typing) and the existing isEditableContext behavior (all INPUT/TEXTAREA are editable). If isEditableTarget is updated to treat these inputs as editable (recommended), this test should be updated to expect the shortcut NOT to trigger.
/andCtrl/Cmd+K) now respectignoreWhenEditable, so they do not fire while focus is in inputs/textareas/contenteditable elements (SearchIconButton.tsx). The manual guard for/remains; the global guard covers both.SearchIconButtontests updated to:/andCtrl/Cmd+K.Ctrl+K(non-macOS) andCmd+K(macOS).ignoreWhenEditable; used here to enforce “no keybinds while typing.”