diff --git a/packages/insomnia-smoke-test/tests/smoke/focus-and-keyboard.test.ts b/packages/insomnia-smoke-test/tests/smoke/focus-and-keyboard.test.ts index 8120641a41..fd1e0ac6f9 100644 --- a/packages/insomnia-smoke-test/tests/smoke/focus-and-keyboard.test.ts +++ b/packages/insomnia-smoke-test/tests/smoke/focus-and-keyboard.test.ts @@ -50,6 +50,22 @@ test.describe('Focus and keyboard navigation', () => { await expect.soft(page.locator(focusedEditorWithChild('key-value-editor__name'))).toHaveCount(1); }); + test('clicking Add moves focus off the Add button so typing lands in the new Name cell', async ({ page }) => { + await page.getByRole('button', { name: 'Create request collection', exact: true }).click(); + + await page.getByRole('tab', { name: 'Params' }).click(); + const addButton = page.getByTestId('request-pane').getByRole('button', { name: 'Add', exact: true }); + await addButton.click(); + + // The Add button keeps focus until the new editor takes it; the autofocus re-grab must not treat + // it as the user having deliberately moved to another control. + await expect.soft(page.locator(focusedEditorWithChild('key-value-editor__name'))).toHaveCount(1); + await expect.soft(addButton).not.toBeFocused(); + + await page.keyboard.type('smoke-key'); + await expect.soft(page.locator('.editor__container:has([id^="key-value-editor__name"]) .CodeMirror-code').first()).toHaveText('smoke-key'); + }); + test('the KV environment editor focuses the blank row Name', async ({ page }) => { await page.getByRole('button', { name: 'Create request collection', exact: true }).click(); diff --git a/packages/insomnia/src/ui/components/.client/codemirror/one-line-editor.tsx b/packages/insomnia/src/ui/components/.client/codemirror/one-line-editor.tsx index 9bb240c513..1b94f950ec 100644 --- a/packages/insomnia/src/ui/components/.client/codemirror/one-line-editor.tsx +++ b/packages/insomnia/src/ui/components/.client/codemirror/one-line-editor.tsx @@ -378,6 +378,11 @@ export const OneLineEditor = forwardRef // moved to another control (e.g. Tab from the URL bar to Send) — until the editor holds focus // or the window elapses. const deadline = Date.now() + 500; + // The control that held focus when we mounted (typically the "Add" button that triggered this + // editor). It keeps focus until we take it, so it must not be mistaken for the user having + // deliberately moved elsewhere. Cleared once the editor holds focus, so a later deliberate + // return to that control is respected. + let initialActiveElement = document.activeElement; const ensureFocus = () => { const cm = codeMirror.current; if (!cm) { @@ -399,6 +404,7 @@ export const OneLineEditor = forwardRef const role = active?.getAttribute('role'); const userMovedToAnotherControl = !!active && + active !== initialActiveElement && (active.tagName === 'INPUT' || active.tagName === 'TEXTAREA' || active.tagName === 'SELECT' || @@ -416,6 +422,8 @@ export const OneLineEditor = forwardRef } cm.focus(); cm.getDoc().setCursor(cm.getDoc().lineCount(), 0); + } else { + initialActiveElement = null; } if (Date.now() < deadline) { requestAnimationFrame(ensureFocus);