Fix QL Copilot thing

This commit is contained in:
shamoon
2026-08-21 13:56:28 -07:00
parent 4c361422ca
commit 7d48b7ea78
2 changed files with 61 additions and 19 deletions
+13 -12
View File
@@ -15,6 +15,16 @@ const MOBILE_BUTTON_POSITIONS = {
"bottom-right": "bottom-4 right-4",
};
function parseUrl(searchString) {
if (!/.+[.:].+/.test(searchString)) return null; // basic test for probably a url
try {
return new URL(searchString.toLowerCase().startsWith("http") ? searchString : `https://${searchString}`);
} catch {
return null;
}
}
function getSearchResults({
hideVisitURL,
searchDescriptions,
@@ -77,7 +87,7 @@ export default function QuickLaunch({ servicesAndBookmarks, searchString, setSea
const searchField = useRef();
const [currentItemIndex, setCurrentItemIndex] = useState(null);
const [url, setUrl] = useState(null);
const url = parseUrl(searchString);
const [searchSuggestions, setSearchSuggestions] = useState([]);
const { data: widgets } = useSWR("/api/widgets");
@@ -134,17 +144,8 @@ export default function QuickLaunch({ servicesAndBookmarks, searchString, setSea
function handleSearchChange(event) {
const rawSearchString = event.target.value;
setCurrentItemIndex(null);
try {
if (!/.+[.:].+/g.test(rawSearchString)) throw new Error(); // basic test for probably a url
let urlString = rawSearchString;
if (urlString.toLowerCase().indexOf("http") !== 0) urlString = `https://${rawSearchString}`;
setUrl(new URL(urlString)); // basic validation
setSearchString(rawSearchString);
return;
} catch (e) {
setUrl(null);
}
setSearchString(rawSearchString.toLowerCase());
// urls keep their casing, everything else is lowercased for matching
setSearchString(parseUrl(rawSearchString) ? rawSearchString : rawSearchString.toLowerCase());
}
function handleSearchKeyDown(event) {
+48 -7
View File
@@ -46,13 +46,25 @@ function Wrapper({ servicesAndBookmarks = [], initialOpen = true } = {}) {
const [isOpen, setSearching] = useState(initialOpen);
return (
<QuickLaunch
servicesAndBookmarks={servicesAndBookmarks}
searchString={searchString}
setSearchString={setSearchString}
isOpen={isOpen}
setSearching={setSearching}
/>
<>
<button
type="button"
data-testid="seed-key"
onClick={() => {
setSearchString((current) => `${current}g`);
setSearching(true);
}}
>
seed
</button>
<QuickLaunch
servicesAndBookmarks={servicesAndBookmarks}
searchString={searchString}
setSearchString={setSearchString}
isOpen={isOpen}
setSearching={setSearching}
/>
</>
);
}
@@ -151,6 +163,35 @@ describe("components/quicklaunch", () => {
openSpy.mockRestore();
});
it("does not carry a previous url result into a search seeded by a keypress", async () => {
renderWithProviders(<Wrapper />, {
settings: {
target: "_self",
quicklaunch: {
provider: "duckduckgo",
showSearchSuggestions: false,
},
},
});
const input = screen.getByPlaceholderText("Search");
await waitFor(() => expect(input).toHaveFocus());
fireEvent.change(input, { target: { value: "example.com" } });
expect(await screen.findByText("quicklaunch.visit URL")).toBeInTheDocument();
fireEvent.keyDown(input, { key: "Escape" });
await act(async () => {
await new Promise((r) => setTimeout(r, 350));
});
// reopen by "typing" a character, the way pages/index does
fireEvent.click(screen.getByTestId("seed-key"));
expect(input).toHaveValue("g");
expect(screen.queryByText("quicklaunch.visit URL")).not.toBeInTheDocument();
});
it("closes on Escape and clears the search string after the timeout", async () => {
renderWithProviders(<Wrapper />, {
settings: {