diff --git a/client-src/elements/chromedash-feature-filter.ts b/client-src/elements/chromedash-feature-filter.ts index 474d9ffe..da958a88 100644 --- a/client-src/elements/chromedash-feature-filter.ts +++ b/client-src/elements/chromedash-feature-filter.ts @@ -26,6 +26,31 @@ class ChromedashFeatureFilter extends LitElement { this.dispatchEvent(event); } + connectedCallback(): void { + super.connectedCallback(); + document.addEventListener('keyup', this.handleDocumentKeyUp); + } + + disconnectedCallback() { + super.disconnectedCallback(); + document.removeEventListener('keyup', this.handleDocumentKeyUp); + } + + handleDocumentKeyUp = (e: KeyboardEvent) => { + const inInputContext = e + .composedPath() + .some(el => + ['INPUT', 'TEXTAREA', 'SL-POPUP', 'SL-DIALOG'].includes( + (el as HTMLElement).tagName + ) + ); + if (e.key === '/' && !inInputContext) { + e.preventDefault(); + e.stopPropagation(); + (this.typeaheadRef?.value as ChromedashTypeahead).focus(); + } + }; + handleSearchClick() { const typeahead = this.typeaheadRef.value; if (!typeahead) return; diff --git a/client-src/elements/chromedash-typeahead.ts b/client-src/elements/chromedash-typeahead.ts index c8afb48c..f4bee431 100644 --- a/client-src/elements/chromedash-typeahead.ts +++ b/client-src/elements/chromedash-typeahead.ts @@ -91,6 +91,16 @@ export class ChromedashTypeahead extends LitElement { this.slDropdownRef.value!.show(); } + focus() { + const slInput: SlInput = this.slInputRef.value as SlInput; + slInput?.focus(); + } + + blur() { + const slInput: SlInput = this.slInputRef.value as SlInput; + slInput?.blur(); + } + findPrefix() { const inputEl = this.slInputRef.value?.input; if (!inputEl) return; diff --git a/packages/playwright/tests/chromedash-new-feature-list.js b/packages/playwright/tests/chromedash-new-feature-list.js new file mode 100644 index 00000000..afcacbce --- /dev/null +++ b/packages/playwright/tests/chromedash-new-feature-list.js @@ -0,0 +1,32 @@ +// @ts-check +import { test, expect } from '@playwright/test'; +import { + captureConsoleMessages, delay, login, logout, + gotoNewFeatureList +} from './test_utils'; + + +test.beforeEach(async ({ page }, testInfo) => { + captureConsoleMessages(page); + testInfo.setTimeout(90000); + + // Login before running each test. + await login(page); +}); + +test.afterEach(async ({ page }) => { + // Logout after running each test. + await logout(page); +}); + +test('Typing slash focuses on searchbox', async ({page}) => { + await gotoNewFeatureList(page, 'http://localhost:5555/'); + const searchbox = page.locator('#inputfield'); + await expect(searchbox).toBeVisible(); + await expect(searchbox).toHaveAttribute('value', ''); + await page.keyboard.type('abc/def/ghi'); + // Characters before the first slash go to the page. + // The slash focuses on the searchbox. + // Later characters, including slashes, go in the searchbox. + await expect(searchbox).toHaveAttribute('value', 'def/ghixx'); +}); diff --git a/packages/playwright/tests/test_utils.js b/packages/playwright/tests/test_utils.js index dd67497f..49579b23 100644 --- a/packages/playwright/tests/test_utils.js +++ b/packages/playwright/tests/test_utils.js @@ -342,3 +342,9 @@ export async function createNewFeature(page) { await page.waitForURL('**/feature/*'); await delay(500); } + +export async function gotoNewFeatureList(page) { + await page.goto('/newfeatures'); + await page.locator('chromedash-feature-pagination'); + await delay(500); +}