diff --git a/libs/web-components/src/common/urls.spec.ts b/libs/web-components/src/common/urls.spec.ts index f7a4919bd..0c9058dc9 100644 --- a/libs/web-components/src/common/urls.spec.ts +++ b/libs/web-components/src/common/urls.spec.ts @@ -44,13 +44,13 @@ it("should match urls", async () => { desc: "empty test url", windowUrl: new URL("http://localhost/foo"), testUrl: "", - weight: 0, + weight: -1, }, { desc: "root path only match", windowUrl: new URL("http://localhost"), testUrl: "/", - weight: 0, + weight: 1, }, { desc: "path match", @@ -121,25 +121,37 @@ it("should match urls", async () => { ]; for (const spec of specs) { - expect(isUrlMatch(spec.windowUrl, spec.testUrl)).toEqual(spec.weight); + try { + expect(isUrlMatch(spec.windowUrl, spec.testUrl)).toEqual(spec.weight); + } catch (error) { + throw new Error(spec.desc); + } + } }); -describe("should getMatchedLink", () => { +interface MenuTest { + desc: string; + windowUrl: URL; + activeMenuHref: string|undefined; +} + +it("should fix bug/1368 getMatchedLink", () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any const links: any[] = [ { - getAttribute: (attr: string) => attr === 'href' ? "#/" : null, + getAttribute: (attr: string) => attr === 'href' ? "/" : null, }, { - getAttribute: (attr: string) => attr === 'href' ? "#/get-started" : null, + getAttribute: (attr: string) => attr === 'href' ? "/get-started" : null, }, { - getAttribute: (attr: string) => attr === 'href' ? "#/tabs" : null, + getAttribute: (attr: string) => attr === 'href' ? "/accordion" : null, }, { getAttribute: (attr: string) => attr === 'href' ? "/patterns" : null, }, + // Make sure external link won't be able to highlighted in any case, even it matched { getAttribute: (attr: string) => { if (attr === 'href') return "https://google.com/choose"; @@ -149,54 +161,45 @@ describe("should getMatchedLink", () => { } ]; + const specs: MenuTest[] = [ + { + desc: "return home menu / if we navigate to /", + windowUrl: new URL("http://localhost/"), + activeMenuHref: "/" + }, + { + desc: "return Get started menu if we navigate to /get-started", + windowUrl: new URL("http://localhost/get-started"), + activeMenuHref: "/get-started" + }, + { + desc: "return Get started menu if we navigate to /get-started/developers", + windowUrl: new URL("http://localhost/get-started/developers"), + activeMenuHref: "/get-started" + }, + { + desc: "return Accordion if we navigate to /accordion#tab-0", + windowUrl: new URL("http://localhost/accordion#tab-0"), + activeMenuHref: "/accordion" + }, + { + desc: "return patterns menu if we navigate to /patterns#tab-1", + windowUrl: new URL("http://localhost/patterns#tab-1"), + activeMenuHref: "/patterns" + }, + { + desc: "return no menu if we navigate to /profile", + windowUrl: new URL("http://localhost/profile"), + activeMenuHref: undefined + } + ]; - it("should return null if we navigate to a home root / (React app)", () => { - const windowUrl = "/"; - const result = getMatchedLink(links, windowUrl); - expect(result).toBeNull(); - }) - - it("should return Home menu if we navigate to a root #/ (Angular app)", () => { - const windowUrl = "/ui-components/#/"; - const result = getMatchedLink(links, windowUrl); - expect(result?.getAttribute("href")).toEqual("#/"); - }); - - it("should return get-started if we navigate to /get-started", () => { - const windowUrl = "/ui-components/#/get-started"; - const result = getMatchedLink(links, windowUrl); - expect(result?.getAttribute("href")).toEqual("#/get-started"); - }); - - it("should return get-started if we navigate to /get-started/developers", () => { - const windowUrl = "/ui-components/#/get-started/developers"; - const result = getMatchedLink(links, windowUrl) - expect(result?.getAttribute("href")).toEqual("#/get-started"); - }); - - it("should return tabs if we navigate to /tabs#tab-0", () => { - const windowUrl = "/ui-components/#/tabs#tab-0"; - const result = getMatchedLink(links, windowUrl); - expect(result?.getAttribute("href")).toEqual("#/tabs"); - }); - - it("should return null if we navigate to /accordion", () => { - const windowUrl = "/ui-components/#/accordion"; - const result = getMatchedLink(links, windowUrl); - console.log(result?.getAttribute("href")); - expect(result).toBeNull(); - }); - - it("should return patterns menu if we navigate to /patterns", () => { - const windowUrl = "/patterns#tab-0"; - const result = getMatchedLink(links, windowUrl); - expect(result?.getAttribute("href")).toEqual("/patterns"); - }); - - it("should return patterns menu if we navigate to /patterns/complex-form", () => { - const windowUrl = "/patterns/complex-form"; - const result = getMatchedLink(links, windowUrl); - expect(result?.getAttribute("href")).toEqual("/patterns"); - }); - + for (const spec of specs) { + const matchedLink = getMatchedLink(links, spec.windowUrl); + try { + expect(matchedLink?.getAttribute("href")).toEqual(spec.activeMenuHref); + } catch (error) { + throw new Error(spec.desc); + } + } }) diff --git a/libs/web-components/src/common/urls.ts b/libs/web-components/src/common/urls.ts index 2bde32db0..6a412adb9 100644 --- a/libs/web-components/src/common/urls.ts +++ b/libs/web-components/src/common/urls.ts @@ -14,17 +14,14 @@ export function isUrlMatch(windowUrl: URL | Location, testUrl: string): number { return 1; } - // root url - if (urlParts.length === 1 && urlParts[0] === "") { - return 0; - } - let weight = -1; let index = 0; - for (const part of windowUrlParts) { - if (urlParts[index] !== part) { - break; + for (const part of urlParts) { + if (windowUrlParts[index] !== part) { + // Ex: windowURl: /get-started/designers should match to a menu "/#/get-started, but not match to "/get-started/developers + // So if we check by menu (linkParts) and have anything not matched, it should return -1 (not matched), otherwise menu /get-started/developers & menu /get-started/ will have the same weight + return -1; } weight += 1; index++; @@ -34,54 +31,12 @@ export function isUrlMatch(windowUrl: URL | Location, testUrl: string): number { return weight >= 0 ? weight + 1 : weight; } -function findMaxIndexMatchedToWindowUrlParts(windowUrlParts: string[], urlParts: string[]) { - - for (let urlPartsIndex = 0; urlPartsIndex < urlParts.length; urlPartsIndex++) { - for (let windowUrlPartsIndex = 0; windowUrlPartsIndex < windowUrlParts.length; windowUrlPartsIndex++) { - const cleanedWindowUrlPart = windowUrlParts[windowUrlPartsIndex].split("#")[0]; - const cleanedUrlPart = urlParts[urlPartsIndex].split("#")[0]; - if (cleanedUrlPart === cleanedWindowUrlPart) { - return windowUrlPartsIndex; - } - } - } - return -1; -} - -function getUrlWeight(windowUrl: string, linkHref: string) { - const windowParts = decodeURIComponent(windowUrl).replace(/^\/#?/, "").split("/"); - const linkParts = decodeURIComponent(linkHref).replace(/^\//, "").split("/"); - - - - let startIndex = findMaxIndexMatchedToWindowUrlParts(windowParts, linkParts); - if (startIndex === -1) { - return -1; - } - // Weight should start with matched index on windowUrl. Ex: window.pathname="/ui-components/#/", linkHref="#/", Home menu should have higher weight than the rest - let weight = startIndex; - - for (let i = 0; i < linkParts.length && startIndex < windowParts.length; i++) { - const cleanedWindowPartStr = windowParts[startIndex].split("#")[0]; - const cleanedLinkPartStr = linkParts[i].split("#")[0]; - if (cleanedWindowPartStr === cleanedLinkPartStr) { - // Increase weight for each matching segment - weight += 1; - } else { - // Break loop on first non-match - break; - } - startIndex++; - } - - return weight; -} -export function getMatchedLink(links: Element[], windowUrl: string) { +export function getMatchedLink(links: Element[], windowUrl: URL | Location) { const weights = links.map((link) => { if (link.getAttribute("target")) return -1; - return getUrlWeight( - windowUrl, + return isUrlMatch( + windowUrl, (link as HTMLLinkElement).getAttribute("href") || "", ) } diff --git a/libs/web-components/src/components/app-header-menu/AppHeaderMenu.svelte b/libs/web-components/src/components/app-header-menu/AppHeaderMenu.svelte index 34a7d3e54..8813f994c 100644 --- a/libs/web-components/src/components/app-header-menu/AppHeaderMenu.svelte +++ b/libs/web-components/src/components/app-header-menu/AppHeaderMenu.svelte @@ -1,14 +1,14 @@ + + + Get started + Overview + UX designers + + Overview + Setup + + diff --git a/libs/web-components/src/components/side-menu/side-menu.spec.ts b/libs/web-components/src/components/side-menu/side-menu.spec.ts index 2a6389383..b4aafafbc 100644 --- a/libs/web-components/src/components/side-menu/side-menu.spec.ts +++ b/libs/web-components/src/components/side-menu/side-menu.spec.ts @@ -1,5 +1,22 @@ -// import SideMenu from "./SideMenu.svelte"; -// import { render } from "@testing-library/svelte"; import { it } from "vitest"; +import { render } from "@testing-library/svelte"; +import SideMenuWrapper from './SideMenuWrapper.test.svelte'; -it.skip("test", async () => { /* do nothing */ }); +describe.skip("SideMenu should render with children and set highlighted menu item correctly", () => { + it("should render", async() => { + // Mock window.location + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + delete window.location; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error + window.location = new URL('http://localhost/get-started'); + const {container} = render(SideMenuWrapper); + + const links = container.querySelectorAll("a"); + expect(links.length).toBe(4); + const currentLink = container.querySelector("a.current"); + expect(currentLink).toBeTruthy(); + expect(currentLink?.getAttribute("href")).toBe("get-started"); + }) +}) diff --git a/libs/web-components/src/components/tabs/Tabs.svelte b/libs/web-components/src/components/tabs/Tabs.svelte index 89c8046f9..55c57377c 100644 --- a/libs/web-components/src/components/tabs/Tabs.svelte +++ b/libs/web-components/src/components/tabs/Tabs.svelte @@ -50,7 +50,7 @@ } function bindChildren() { - const path = window.location.href; + const path = window.location.pathname; // create buttons (tabs) for each of the tab contents elements _tabProps.forEach((tabProps, index) => {