fix(autocomplete): add keyboard navigation and fix scroll/blur in all CODMAT dropdowns
Extract shared setupAutocomplete() into shared.js so all three autocomplete instances (mappings modal, inline add, quick-map modal) get keyboard nav (ArrowDown/Up/Enter/Escape), scroll-safe blur handling, and capture-phase keydown to prevent browser interception. Remove old onmousedown inline handlers, use data-codmat/data-label attributes instead. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
"""E2E: Mappings page with flat-row list, sorting, multi-CODMAT modal."""
|
||||
import re
|
||||
|
||||
import pytest
|
||||
from playwright.sync_api import Page, expect
|
||||
|
||||
@@ -77,3 +79,100 @@ def test_inline_add_button_exists(page: Page):
|
||||
"""Verify 'Adauga Mapare' button is present."""
|
||||
btn = page.locator("button", has_text="Adauga Mapare")
|
||||
expect(btn).to_be_visible()
|
||||
|
||||
|
||||
# ── Autocomplete keyboard & scroll tests ─────────
|
||||
|
||||
MOCK_ARTICLES = [
|
||||
{"codmat": f"ART{i:03}", "denumire": f"Articol Test {i}", "um": "BUC"}
|
||||
for i in range(1, 20)
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_articles(page: Page):
|
||||
"""Mock /api/articles/search to return test data without Oracle."""
|
||||
def handle(route):
|
||||
route.fulfill(json={"results": MOCK_ARTICLES})
|
||||
page.route("**/api/articles/search*", handle)
|
||||
yield
|
||||
page.unroute("**/api/articles/search*")
|
||||
|
||||
|
||||
def _open_modal_and_type(page: Page, query: str = "ART"):
|
||||
"""Open add-modal, type in CODMAT input, wait for dropdown."""
|
||||
page.locator("button[data-bs-target='#addModal']").first.click()
|
||||
page.wait_for_timeout(400)
|
||||
codmat_input = page.locator("#codmatLines .cl-codmat").first
|
||||
codmat_input.fill(query)
|
||||
# Wait for debounce + render
|
||||
page.wait_for_timeout(400)
|
||||
return codmat_input
|
||||
|
||||
|
||||
def test_autocomplete_keyboard_navigation(page: Page, mock_articles):
|
||||
"""ArrowDown/Up moves .active class, Enter selects."""
|
||||
codmat_input = _open_modal_and_type(page)
|
||||
|
||||
dropdown = page.locator("#codmatLines .cl-ac-dropdown").first
|
||||
expect(dropdown).to_be_visible()
|
||||
|
||||
# ArrowDown → first item active
|
||||
codmat_input.press("ArrowDown")
|
||||
first_item = dropdown.locator(".autocomplete-item").first
|
||||
expect(first_item).to_have_class(re.compile("active"))
|
||||
|
||||
# ArrowDown again → second item active
|
||||
codmat_input.press("ArrowDown")
|
||||
second_item = dropdown.locator(".autocomplete-item").nth(1)
|
||||
expect(second_item).to_have_class(re.compile("active"))
|
||||
expect(first_item).not_to_have_class(re.compile("active"))
|
||||
|
||||
# ArrowUp → back to first
|
||||
codmat_input.press("ArrowUp")
|
||||
expect(first_item).to_have_class(re.compile("active"))
|
||||
|
||||
# Enter → selects the item
|
||||
codmat_input.press("Enter")
|
||||
expect(dropdown).to_be_hidden()
|
||||
assert codmat_input.input_value() == "ART001"
|
||||
|
||||
|
||||
def test_autocomplete_escape_closes(page: Page, mock_articles):
|
||||
"""Escape closes dropdown."""
|
||||
codmat_input = _open_modal_and_type(page)
|
||||
|
||||
dropdown = page.locator("#codmatLines .cl-ac-dropdown").first
|
||||
expect(dropdown).to_be_visible()
|
||||
|
||||
codmat_input.press("Escape")
|
||||
expect(dropdown).to_be_hidden()
|
||||
|
||||
|
||||
def test_autocomplete_scroll_keeps_open(page: Page, mock_articles):
|
||||
"""Mouse wheel on dropdown doesn't close it (blur fix)."""
|
||||
codmat_input = _open_modal_and_type(page)
|
||||
|
||||
dropdown = page.locator("#codmatLines .cl-ac-dropdown").first
|
||||
expect(dropdown).to_be_visible()
|
||||
|
||||
# Scroll inside the dropdown via mouse wheel
|
||||
dropdown.evaluate("el => el.scrollTop = 100")
|
||||
page.wait_for_timeout(300)
|
||||
|
||||
# Dropdown should still be visible
|
||||
expect(dropdown).to_be_visible()
|
||||
|
||||
|
||||
def test_autocomplete_click_outside_closes(page: Page, mock_articles):
|
||||
"""Click outside closes dropdown (Tab away moves focus)."""
|
||||
codmat_input = _open_modal_and_type(page)
|
||||
|
||||
dropdown = page.locator("#codmatLines .cl-ac-dropdown").first
|
||||
expect(dropdown).to_be_visible()
|
||||
|
||||
# Tab away from the input to trigger blur
|
||||
codmat_input.press("Tab")
|
||||
page.wait_for_timeout(300)
|
||||
|
||||
expect(dropdown).to_be_hidden()
|
||||
|
||||
Reference in New Issue
Block a user