From 47ae056e14936a86a67a2ac1f8fc05e3a6eeb337 Mon Sep 17 00:00:00 2001 From: Parag Jain Date: Fri, 5 Jun 2026 15:16:24 +0530 Subject: [PATCH 1/2] handle alert explore names with spaces, hyphens etc. --- .../features/explore-mappers/utils.spec.ts | 34 +++++++++++++++++++ .../src/features/explore-mappers/utils.ts | 5 ++- 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 web-common/src/features/explore-mappers/utils.spec.ts 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 000000000000..55cb2c1bf9eb --- /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 10209f0dc076..54d5c0396a4e 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); From 030a62bff41c73e4dfbca62663923266c2cfb369 Mon Sep 17 00:00:00 2001 From: Parag Jain Date: Fri, 5 Jun 2026 15:27:32 +0530 Subject: [PATCH 2/2] prettier --- web-common/src/features/explore-mappers/utils.spec.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/web-common/src/features/explore-mappers/utils.spec.ts b/web-common/src/features/explore-mappers/utils.spec.ts index 55cb2c1bf9eb..0f3eb05c49fb 100644 --- a/web-common/src/features/explore-mappers/utils.spec.ts +++ b/web-common/src/features/explore-mappers/utils.spec.ts @@ -19,9 +19,9 @@ describe("getExploreName", () => { }); it("stops at a query string boundary", () => { - expect(getExploreName("/explore/My%20Explore?execution_time=2026-06-04")).toBe( - "My Explore", - ); + expect( + getExploreName("/explore/My%20Explore?execution_time=2026-06-04"), + ).toBe("My Explore"); }); it("stops at a trailing slash", () => {