diff --git a/src/components/shared/EditorVersionToggle.test.tsx b/src/components/shared/EditorVersionToggle.test.tsx
new file mode 100644
index 000000000..a385af74b
--- /dev/null
+++ b/src/components/shared/EditorVersionToggle.test.tsx
@@ -0,0 +1,81 @@
+import { fireEvent, render, screen } from "@testing-library/react";
+import { beforeEach, describe, expect, it, vi } from "vitest";
+
+import { EditorVersionToggle } from "./EditorVersionToggle";
+
+const mockNavigate = vi.fn();
+let mockPathname = "/";
+let mockFlagEnabled = true;
+
+vi.mock("@tanstack/react-router", async (importOriginal) => ({
+ ...(await importOriginal()),
+ useNavigate: () => mockNavigate,
+ useLocation: () => ({ pathname: mockPathname }),
+}));
+
+vi.mock("./Settings/useFlags", () => ({
+ useFlagValue: () => mockFlagEnabled,
+}));
+
+describe("EditorVersionToggle", () => {
+ beforeEach(() => {
+ mockNavigate.mockClear();
+ mockPathname = "/";
+ mockFlagEnabled = true;
+ });
+
+ it("renders nothing when the v2_editor flag is disabled", () => {
+ mockFlagEnabled = false;
+ mockPathname = "/editor/my-pipeline";
+
+ render();
+
+ expect(screen.queryByRole("button")).toBeNull();
+ });
+
+ it("renders nothing on routes without a v1/v2 counterpart", () => {
+ mockPathname = "/pipelines";
+
+ render();
+
+ expect(screen.queryByRole("button")).toBeNull();
+ });
+
+ it.each([
+ ["/editor/my-pipeline", "/editor-v2/my-pipeline", "Switch to new editor"],
+ ["/runs/run-123", "/runs-v2/run-123", "Switch to new view"],
+ ["/runs/run-123/sub-456", "/runs-v2/run-123/sub-456", "Switch to new view"],
+ ])("switches %s to the new version (%s)", (from, to, label) => {
+ mockPathname = from;
+
+ render();
+
+ const button = screen.getByRole("button", { name: label });
+ fireEvent.click(button);
+
+ expect(mockNavigate).toHaveBeenCalledWith({ to });
+ });
+
+ it.each([
+ [
+ "/editor-v2/my-pipeline",
+ "/editor/my-pipeline",
+ "Switch to legacy editor",
+ ],
+ ["/runs-v2/run-123", "/runs/run-123", "Switch to legacy view"],
+ [
+ "/runs-v2/run-123/sub-456",
+ "/runs/run-123/sub-456",
+ "Switch to legacy view",
+ ],
+ ])("switches %s to the legacy version (%s)", (from, to, label) => {
+ mockPathname = from;
+
+ render();
+
+ const button = screen.getByRole("button", { name: label });
+ fireEvent.click(button);
+
+ expect(mockNavigate).toHaveBeenCalledWith({ to });
+ });
+});
diff --git a/src/components/shared/EditorVersionToggle.tsx b/src/components/shared/EditorVersionToggle.tsx
index 35ced979e..47c61cc80 100644
--- a/src/components/shared/EditorVersionToggle.tsx
+++ b/src/components/shared/EditorVersionToggle.tsx
@@ -2,15 +2,44 @@ import { useLocation, useNavigate } from "@tanstack/react-router";
import TooltipButton from "@/components/shared/Buttons/TooltipButton";
import { Icon } from "@/components/ui/icon";
-import { APP_ROUTES, EDITOR_PATH } from "@/routes/router";
+import { APP_ROUTES, EDITOR_PATH } from "@/routes/appRoutes";
import { useFlagValue } from "./Settings/useFlags";
type EditorVersion = "v1" | "v2";
-const detectEditorVersion = (pathname: string): EditorVersion | null => {
- if (pathname.startsWith(`${APP_ROUTES.EDITOR_V2}/`)) return "v2";
- if (pathname.startsWith(`${EDITOR_PATH}/`)) return "v1";
+const ROUTE_BASE_PAIRS: { v1: string; v2: string; noun: string }[] = [
+ { v1: EDITOR_PATH, v2: APP_ROUTES.EDITOR_V2, noun: "editor" },
+ { v1: APP_ROUTES.RUNS, v2: APP_ROUTES.RUNS_V2, noun: "view" },
+];
+
+type ToggleTarget = {
+ to: string;
+ tooltip: string;
+ targetVersion: EditorVersion;
+};
+
+const getToggleTarget = (pathname: string): ToggleTarget | null => {
+ for (const { v1, v2, noun } of ROUTE_BASE_PAIRS) {
+ if (pathname.startsWith(`${v2}/`)) {
+ const rest = pathname.slice(v2.length + 1);
+ if (!rest) return null;
+ return {
+ to: `${v1}/${rest}`,
+ tooltip: `Switch to legacy ${noun}`,
+ targetVersion: "v1",
+ };
+ }
+ if (pathname.startsWith(`${v1}/`)) {
+ const rest = pathname.slice(v1.length + 1);
+ if (!rest) return null;
+ return {
+ to: `${v2}/${rest}`,
+ tooltip: `Switch to new ${noun}`,
+ targetVersion: "v2",
+ };
+ }
+ }
return null;
};
@@ -21,28 +50,16 @@ export const EditorVersionToggle = () => {
if (!isEnabled) return null;
- const version = detectEditorVersion(location.pathname);
- if (!version) return null;
-
- const lastSegment = location.pathname.split("/").pop() ?? "";
- const pipelineName = decodeURIComponent(lastSegment);
- if (!pipelineName) return null;
-
- const targetVersion = version === "v1" ? "v2" : "v1";
- const targetPath =
- targetVersion === "v2"
- ? `${APP_ROUTES.EDITOR_V2}/${encodeURIComponent(pipelineName)}`
- : `${EDITOR_PATH}/${encodeURIComponent(pipelineName)}`;
- const tooltip =
- targetVersion === "v2" ? "Switch to new editor" : "Switch to legacy editor";
+ const target = getToggleTarget(location.pathname);
+ if (!target) return null;
return (
navigate({ to: targetPath })}
- aria-label={tooltip}
+ tooltip={target.tooltip}
+ onClick={() => navigate({ to: target.to })}
+ aria-label={target.tooltip}
>
-
+
);
};