fix(ui): provide data router for navigation guards

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
DottaandPaperclip committed 2026-07-22 19:30:26 +00:00
1 parent 77faf1744e
commit 18860faf4a
4 files changed
+76 -32

No files matched your search

+38
View File
@@ -0,0 +1,38 @@
// @vitest-environment jsdom
import { flushSync } from "react-dom";
import { createRoot, type Root } from "react-dom/client";
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { RouterProvider, useBlocker } from "@/lib/router";
import { createAppRouter } from "./app-router";
describe("createAppRouter", () => {
let container: HTMLDivElement;
let root: Root;
beforeEach(() => {
window.history.replaceState(null, "", "/agents/test/configuration");
container = document.createElement("div");
document.body.appendChild(container);
root = createRoot(container);
});
afterEach(() => {
flushSync(() => root.unmount());
container.remove();
});
it("provides the data-router context required by navigation blockers", async () => {
function BlockerProbe() {
const blocker = useBlocker(true);
return <span>{blocker.state}</span>;
}
const router = createAppRouter(<BlockerProbe />);
flushSync(() => root.render(<RouterProvider router={router} />));
expect(container.textContent).toBe("unblocked");
router.dispose();
});
});
+11
View File
@@ -0,0 +1,11 @@
import type { ReactNode } from "react";
import { createBrowserRouter } from "@/lib/router";
export function createAppRouter(element: ReactNode) {
return createBrowserRouter([
{
path: "*",
element,
},
]);
}
+27 -24
View File
@@ -2,7 +2,7 @@ import * as React from "react";
import { StrictMode } from "react";
import * as ReactDOM from "react-dom";
import { createRoot } from "react-dom/client";
import { BrowserRouter } from "@/lib/router";
import { RouterProvider } from "@/lib/router";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { App } from "./App";
import { CompanyProvider, useCompany } from "./context/CompanyContext";
@@ -18,6 +18,7 @@ import { TooltipProvider } from "@/components/ui/tooltip";
import { initPluginBridge } from "./plugins/bridge-init";
import { PluginLauncherProvider } from "./plugins/launchers";
import { startPerfMeasureReaper } from "./lib/perf-measure-reaper";
import { createAppRouter } from "./lib/app-router";
import "@mdxeditor/editor/style.css";
import "./index.css";
@@ -52,33 +53,35 @@ function CompanyAwareBreadcrumbProvider({ children }: { children: React.ReactNod
return <BreadcrumbProvider companyName={selectedCompany?.name ?? null}>{children}</BreadcrumbProvider>;
}
const router = createAppRouter(
<CompanyProvider>
<EditorAutocompleteProvider>
<ToastProvider>
<LiveUpdatesProvider>
<TooltipProvider>
<CompanyAwareBreadcrumbProvider>
<SidebarProvider>
<PanelProvider>
<PluginLauncherProvider>
<DialogProvider>
<App />
</DialogProvider>
</PluginLauncherProvider>
</PanelProvider>
</SidebarProvider>
</CompanyAwareBreadcrumbProvider>
</TooltipProvider>
</LiveUpdatesProvider>
</ToastProvider>
</EditorAutocompleteProvider>
</CompanyProvider>,
);
createRoot(document.getElementById("root")!).render(
<StrictMode>
<QueryClientProvider client={queryClient}>
<ThemeProvider>
<BrowserRouter>
<CompanyProvider>
<EditorAutocompleteProvider>
<ToastProvider>
<LiveUpdatesProvider>
<TooltipProvider>
<CompanyAwareBreadcrumbProvider>
<SidebarProvider>
<PanelProvider>
<PluginLauncherProvider>
<DialogProvider>
<App />
</DialogProvider>
</PluginLauncherProvider>
</PanelProvider>
</SidebarProvider>
</CompanyAwareBreadcrumbProvider>
</TooltipProvider>
</LiveUpdatesProvider>
</ToastProvider>
</EditorAutocompleteProvider>
</CompanyProvider>
</BrowserRouter>
<RouterProvider router={router} />
</ThemeProvider>
</QueryClientProvider>
</StrictMode>
-8
View File
@@ -1061,14 +1061,6 @@ export function AgentDetail() {
});
}, [resolvedAgentId, agentMembershipState]);
useBeforeUnload(
useCallback((event) => {
if (!configDirty) return;
event.preventDefault();
event.returnValue = "";
}, [configDirty]),
);
if (isLoading) return <PageSkeleton variant="detail" />;
if (error) return <p className="text-sm text-destructive">{error.message}</p>;
if (!agent) return null;