Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions e2e/tests/file-search.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,43 @@ test.describe("file search", () => {
await expect(page).toHaveURL(/\/README\.md$/);
});

test("Shift+Enter opens the selected file in a new tab, leaving the modal open", async ({
page,
}) => {
await page.goto("/README.md");
await page.keyboard.press("Meta+K");
await page.keyboard.type("runb");

const dialog = page.locator("dialog.mdbrowse-search");
const [popup] = await Promise.all([
page.context().waitForEvent("page"),
page.keyboard.press("Shift+Enter"),
]);

await popup.waitForLoadState();
await expect(popup).toHaveURL(/\/runbooks\/deploy\.md$/);

// Original tab stayed put and the modal is still open for the next pick.
await expect(page).toHaveURL(/\/README\.md$/);
await expect(dialog).toBeVisible();
});

test("Cmd+Click opens the result in a new tab", async ({ page }) => {
await page.goto("/README.md");
await page.keyboard.press("Meta+K");
await page.keyboard.type("runb");

const first = page.locator("dialog.mdbrowse-search li.mdbrowse-search__result").first();
const [popup] = await Promise.all([
page.context().waitForEvent("page"),
first.click({ modifiers: ["Meta"] }),
]);

await popup.waitForLoadState();
await expect(popup).toHaveURL(/\/runbooks\/deploy\.md$/);
await expect(page).toHaveURL(/\/README\.md$/);
});

test("ArrowDown moves selection and Enter follows it", async ({ page }) => {
await page.goto("/README.md");
await page.keyboard.press("Meta+K");
Expand Down
23 changes: 18 additions & 5 deletions src/assets/static/js/file-search.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@
moveSelection(-1);
} else if (e.key === "Enter") {
e.preventDefault();
navigateToSelected();
// Shift+Enter opens the result in a new tab and leaves the modal open,
// so you can fan out to several files in one pass.
navigateToSelected(e.shiftKey);
}
// Escape is handled natively by <dialog>.
});
Expand All @@ -73,7 +75,9 @@
var li = e.target instanceof Element ? e.target.closest("li") : null;
if (!li) return;
var path = li.getAttribute("data-path");
if (path) location.assign(path);
if (!path) return;
// Cmd/Ctrl+Click mirrors Shift+Enter: open in a new tab.
open(path, e.metaKey || e.ctrlKey);
});

function openDialog() {
Expand Down Expand Up @@ -141,9 +145,18 @@
}
}

function navigateToSelected() {
function navigateToSelected(newTab) {
var entry = current[selectedIdx];
if (entry) location.assign(entry.path);
if (entry) open(entry.path, newTab);
}

// Navigate to `path`. When `newTab` is truthy, open it in a new tab and keep
// the current page (and the search modal) intact; otherwise replace the
// current page. window.open() here runs synchronously inside a keydown/click
// handler, so it counts as a user gesture and won't trip popup blockers.
function open(path, newTab) {
if (newTab) window.open(path, "_blank");
else location.assign(path);
}

/**
Expand Down Expand Up @@ -204,7 +217,7 @@
d.innerHTML =
'<div class="mdbrowse-search__input-row">' +
'<input class="mdbrowse-search__input" type="text" autocomplete="off" autocapitalize="off" spellcheck="false" placeholder="Type to search files…" aria-label="Search files" />' +
'<span class="mdbrowse-search__hint">Esc to close</span>' +
'<span class="mdbrowse-search__hint">⇧↵ new tab · Esc close</span>' +
"</div>" +
'<ul class="mdbrowse-search__results" role="listbox"></ul>' +
'<div class="mdbrowse-search__empty" hidden>No matches</div>';
Expand Down
Loading