diff --git a/src/components/quicklaunch.jsx b/src/components/quicklaunch.jsx
index 7375259ce..f6e12c430 100644
--- a/src/components/quicklaunch.jsx
+++ b/src/components/quicklaunch.jsx
@@ -124,8 +124,8 @@ export default function QuickLaunch({ servicesAndBookmarks, searchString, setSea
? MOBILE_BUTTON_POSITIONS[settings.quicklaunch.mobileButtonPosition]
: null;
- function openCurrentItem(newWindow) {
- const result = results[activeItemIndex];
+ function openCurrentItem(newWindow, index = activeItemIndex) {
+ const result = results[index];
window.open(
result.href,
newWindow ? "_blank" : (result.target ?? searchProvider?.target ?? settings.target ?? "_blank"),
@@ -180,7 +180,8 @@ export default function QuickLaunch({ servicesAndBookmarks, searchString, setSea
function handleItemClick(event) {
closeAndReset();
- openCurrentItem(event.metaKey);
+ // in case hover doesnt fire, use the clicked item, not the highlighted one
+ openCurrentItem(event.metaKey, parseInt(event.currentTarget.dataset.index, 10));
}
function handleItemKeyDown(event) {
diff --git a/src/components/quicklaunch.test.jsx b/src/components/quicklaunch.test.jsx
index 031471916..02bba7ac1 100644
--- a/src/components/quicklaunch.test.jsx
+++ b/src/components/quicklaunch.test.jsx
@@ -263,6 +263,37 @@ describe("components/quicklaunch", () => {
openSpy.mockRestore();
});
+ it("opens the clicked result even without a preceding hover event", async () => {
+ const openSpy = vi.spyOn(window, "open").mockImplementation(() => null);
+
+ renderWithProviders(
+ ,
+ { settings: { target: "_self", quicklaunch: { showSearchSuggestions: false } } },
+ );
+
+ const input = screen.getByPlaceholderText("Search");
+ await waitFor(() => expect(input).toHaveFocus());
+
+ fireEvent.change(input, { target: { value: "al" } });
+ await waitFor(() => expect(document.querySelector('button[data-index="2"]')).toBeTruthy());
+
+ // touch devices don't fire mouseEnter, so the highlighted item is still the first one
+ fireEvent.click(document.querySelector('button[data-index="2"]'));
+
+ await act(async () => {
+ await new Promise((r) => setTimeout(r, 350));
+ });
+
+ expect(openSpy).toHaveBeenCalledWith("https://almond.example", "_self", "noreferrer");
+ openSpy.mockRestore();
+ });
+
it("handles Escape on a result button (not just the input)", async () => {
renderWithProviders(, {
settings: { quicklaunch: { showSearchSuggestions: false } },