diff --git a/web-common/src/features/explore-mappers/utils.spec.ts b/web-common/src/features/explore-mappers/utils.spec.ts new file mode 100644 index 00000000000..0f3eb05c49f --- /dev/null +++ b/web-common/src/features/explore-mappers/utils.spec.ts @@ -0,0 +1,34 @@ +import { getExploreName } from "@rilldata/web-common/features/explore-mappers/utils.ts"; +import { describe, expect, it } from "vitest"; + +describe("getExploreName", () => { + it("decodes a simple explore name", () => { + expect(getExploreName("/explore/my_explore")).toBe("my_explore"); + }); + + it("decodes spaces encoded as %20", () => { + expect(getExploreName("/explore/My%20Explore")).toBe("My Explore"); + }); + + it("preserves parentheses and hyphens (does not truncate at non-word characters)", () => { + expect( + getExploreName( + "/explore/Sales%20Overview%20-%20Region%20(EMEA%20under%20review)", + ), + ).toBe("Sales Overview - Region (EMEA under review)"); + }); + + it("stops at a query string boundary", () => { + expect( + getExploreName("/explore/My%20Explore?execution_time=2026-06-04"), + ).toBe("My Explore"); + }); + + it("stops at a trailing slash", () => { + expect(getExploreName("/explore/My%20Explore/")).toBe("My Explore"); + }); + + it("returns an empty string when the path has no explore segment", () => { + expect(getExploreName("/canvas/my_canvas")).toBe(""); + }); +}); diff --git a/web-common/src/features/explore-mappers/utils.ts b/web-common/src/features/explore-mappers/utils.ts index 10209f0dc07..54d5c0396a4 100644 --- a/web-common/src/features/explore-mappers/utils.ts +++ b/web-common/src/features/explore-mappers/utils.ts @@ -112,7 +112,10 @@ export async function fillTimeRange( } } -const ExploreNameRegex = /\/explore\/((?:[\w-]|%[0-9A-Fa-f]{2})+)/; +// Capture everything after "/explore/" up to the next path, query, or hash boundary. +// This mirrors the backend's exploreNameFromAnnotations, which decodes the entire segment +// rather than restricting to word characters (explore names can contain spaces, parentheses, etc.). +const ExploreNameRegex = /\/explore\/([^/?#]+)/; export function getExploreName(webOpenPath: string) { const matches = ExploreNameRegex.exec(webOpenPath);