Jump to
Type to search. Use up and down arrow keys to move through results, Enter to select a result, and Escape to close this menu.
@@ -284,11 +59,10 @@ class NavigationSearch extends HTMLElement {
}
setupEventListeners() {
- const dialog = this.shadowRoot.querySelector("dialog");
- const input = this.shadowRoot.querySelector(".search-input");
- const closeButton = this.shadowRoot.querySelector(".close-search");
- const resultsContainer =
- this.shadowRoot.querySelector(".results-container");
+ const dialog = this.querySelector("dialog");
+ const input = this.querySelector(".search-input");
+ const closeButton = this.querySelector(".close-search");
+ const resultsContainer = this.querySelector(".results-container");
// Global keyboard listener for "/"
document.addEventListener("keydown", (e) => {
@@ -408,8 +182,8 @@ class NavigationSearch extends HTMLElement {
}
updateComboboxState() {
- const dialog = this.shadowRoot.querySelector("dialog");
- const input = this.shadowRoot.querySelector(".search-input");
+ const dialog = this.querySelector("dialog");
+ const input = this.querySelector(".search-input");
const matches = this.renderedMatches || [];
this.setElementAttribute(
input,
@@ -434,7 +208,7 @@ class NavigationSearch extends HTMLElement {
}
setStatus(message) {
- const status = this.shadowRoot.querySelector(`#${this.statusId}`);
+ const status = this.querySelector(`#${this.statusId}`);
if (status) {
status.textContent = message || "";
}
@@ -644,7 +418,7 @@ class NavigationSearch extends HTMLElement {
section.render(node, {
navigationSearch: this,
container,
- input: this.shadowRoot.querySelector(".search-input"),
+ input: this.querySelector(".search-input"),
});
});
}
@@ -683,8 +457,8 @@ class NavigationSearch extends HTMLElement {
}
renderResults() {
- const container = this.shadowRoot.querySelector(".results-container");
- const input = this.shadowRoot.querySelector(".search-input");
+ const container = this.querySelector(".results-container");
+ const input = this.querySelector(".search-input");
const showStartContent = !input.value.trim();
const jumpSections = showStartContent ? this.jumpSections() : [];
const startBlock = showStartContent
@@ -797,11 +571,12 @@ class NavigationSearch extends HTMLElement {
}
openMenu(trigger) {
- const input = this.shadowRoot.querySelector(".search-input");
+ const input = this.querySelector(".search-input");
- this.shadowRoot
- .querySelector("datasette-modal")
- .show({ trigger, initialFocus: input });
+ this.querySelector("datasette-modal").show({
+ trigger,
+ initialFocus: input,
+ });
this.setNavigationTriggersExpanded(true);
input.value = "";
@@ -813,11 +588,11 @@ class NavigationSearch extends HTMLElement {
}
closeMenu(options = {}) {
- this.shadowRoot.querySelector("datasette-modal").close(options);
+ this.querySelector("datasette-modal").close(options);
}
onMenuClosed() {
- const input = this.shadowRoot.querySelector(".search-input");
+ const input = this.querySelector(".search-input");
this.setElementAttribute(input, "aria-expanded", "false");
this.removeElementAttribute(input, "aria-activedescendant");
this.setNavigationTriggersExpanded(false);
diff --git a/tests/test_playwright.py b/tests/test_playwright.py
index 753691e4..890ba076 100644
--- a/tests/test_playwright.py
+++ b/tests/test_playwright.py
@@ -1083,6 +1083,41 @@ def test_navigation_search_renders_jump_sections_from_javascript_plugins(
page.wait_for_url("**/-/playwright-agent")
+@pytest.mark.playwright
+def test_navigation_search_created_from_javascript(page, datasette_server):
+ from playwright.sync_api import expect
+
+ page.goto(datasette_server)
+ page.evaluate("""() => {
+ const search = document.createElement('navigation-search');
+ search.id = 'additional-search';
+ search.setAttribute('items', JSON.stringify([
+ {name: 'Projects', url: '/data/projects'}
+ ]));
+ document.body.append(search);
+ const unrelated = document.createElement('div');
+ unrelated.className = 'search-container';
+ unrelated.id = 'outside-search';
+ document.body.append(unrelated);
+ search.openMenu();
+ }""")
+ search = page.locator("#additional-search")
+ dialog = search.get_by_role("dialog", name="Jump to", exact=True)
+ expect(dialog).to_be_visible()
+ # Page styles and ordinary DOM queries can reach the component's controls.
+ page.add_style_tag(
+ content="#additional-search .search-input { border-top-color: rgb(1, 2, 3); }"
+ )
+ field = dialog.get_by_role("combobox", name="Jump to", exact=True)
+ expect(field).to_have_css("border-top-color", "rgb(1, 2, 3)")
+ assert field.evaluate("node => document.getElementById(node.id) === node")
+ expect(page.locator("#outside-search")).to_have_css("display", "block")
+ field.fill("projects")
+ expect(dialog.get_by_role("option")).to_contain_text("Projects")
+ field.press("Enter")
+ page.wait_for_url("**/data/projects")
+
+
@pytest.mark.playwright
def test_insert_row_flow_uses_custom_column_field(page, datasette_server):
page.add_init_script("""