-
-
Notifications
You must be signed in to change notification settings - Fork 96
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
maint(pat navigationmarker): Simplify and improve the implementation.
- Loading branch information
Showing
2 changed files
with
211 additions
and
39 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,54 +1,87 @@ | ||
import $ from "jquery"; | ||
import Base from "@patternslib/patternslib/src/core/base"; | ||
import Parser from "@patternslib/patternslib/src/core/parser"; | ||
|
||
export const parser = new Parser("navigation"); | ||
parser.addArgument("item-wrapper", null); | ||
parser.addArgument("in-path-class", "inPath"); | ||
parser.addArgument("current-class", "current"); | ||
|
||
export default Base.extend({ | ||
name: "navigationmarker", | ||
trigger: ".pat-navigationmarker", | ||
parser: "mockup", | ||
init() { | ||
const portal_url = document.body.dataset.portalUrl; | ||
const href = | ||
document.querySelector('head link[rel="canonical"]').href || | ||
window.location.href; | ||
const hrefParts = href.split("/"); | ||
this.options = parser.parse(this.el, this.options); | ||
this.mark_items(); | ||
}, | ||
|
||
mark_items(url) { | ||
// Mark all navigation items that are in the path of the current url | ||
|
||
const current_url = url || this.base_url(); | ||
const current_url_prepared = this.prepare_url(current_url); | ||
|
||
const portal_url = this.prepare_url(document.body.dataset?.portalUrl); | ||
const nav_items = this.el.querySelectorAll("a"); | ||
|
||
for (const nav_item of nav_items) { | ||
const navlink = nav_item.getAttribute("href", "").replace("/view", ""); | ||
if (href.indexOf(navlink) !== -1) { | ||
const parent = $(nav_item).parent(); | ||
|
||
// check the input-openers within the path | ||
const check = parent.find("> input"); | ||
if (check.length) { | ||
check[0].checked = true; | ||
} | ||
|
||
// set "inPath" to all nav items which are within the current path | ||
// check if parts of navlink are in canonical url parts | ||
const navParts = navlink.split("/"); | ||
let inPath = false; | ||
for (let i = 0, size = navParts.length; i < size; i++) { | ||
// The last path-part must match. | ||
inPath = false; | ||
if (navParts[i] === hrefParts[i]) { | ||
inPath = true; | ||
} | ||
} | ||
if (navlink === portal_url && href !== portal_url) { | ||
// Avoid marking "Home" with "inPath", when not actually there | ||
inPath = false; | ||
} | ||
if (inPath) { | ||
parent.addClass("inPath"); | ||
} | ||
|
||
// set "current" to the current selected nav item, if it is in the navigation structure. | ||
if (href === navlink) { | ||
parent.addClass("current"); | ||
} | ||
// Get the nav item's url and rebase it against the current url to | ||
// make absolute or relative URLs FQDN URLs. | ||
const nav_url = this.prepare_url( | ||
new URL(nav_item.getAttribute("href", ""), current_url)?.href | ||
); | ||
|
||
const wrapper = this.options.itemWrapper | ||
? nav_item.closest(this.options.itemWrapper) | ||
: nav_item.parentNode; | ||
|
||
if (nav_url === current_url_prepared) { | ||
wrapper.classList.add(this.options.currentClass); | ||
} else if ( | ||
// Compare the current navigation item url with a slash at the | ||
// end - if it is "inPath" it must have a slash in it. | ||
current_url_prepared.indexOf(`${nav_url}/`) === 0 && | ||
// Do not set inPath for the "Home" url, as this would always | ||
// be in the path. | ||
nav_url !== portal_url | ||
) { | ||
wrapper.classList.add(this.options.inPathClass); | ||
} else { | ||
// Not even in path. | ||
continue; | ||
} | ||
|
||
// The path was at least found in the current url, so we need to | ||
// check the input-openers within the path | ||
// Find the first input which is the correct one, even if this | ||
// navigation item has many children. | ||
// These hidden checkboxes are used to open the navigation item for | ||
// mobile navigation. | ||
const check = wrapper.querySelector("input"); | ||
if (check) check.checked = true; | ||
} | ||
}, | ||
|
||
clear_items() { | ||
// Clear all navigation items from the inPath and current classes | ||
|
||
const items = this.el.querySelectorAll( | ||
`.${this.options.inPathClass}, .${this.options.currentClass}` | ||
); | ||
for (const item of items) { | ||
item.classList.remove(this.options.inPathClass); | ||
item.classList.remove(this.options.currentClass); | ||
} | ||
}, | ||
|
||
prepare_url(url) { | ||
return url?.replace("/view", "").replaceAll("@@", "").replace(/\/$/, ""); | ||
}, | ||
|
||
base_url() { | ||
return this.prepare_url( | ||
document.querySelector('head link[rel="canonical"]')?.href || | ||
window.location.href | ||
); | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
import Pattern from "./navigationmarker"; | ||
|
||
describe("pat-navigationmarker", () => { | ||
let _window_location; | ||
|
||
beforeEach(() => { | ||
_window_location = global.window.location; | ||
delete global.window.location; | ||
document.body.innerHTML = ""; | ||
}); | ||
|
||
afterEach(() => { | ||
global.window.location = _window_location; | ||
}); | ||
|
||
const set_url = (url, portal_url) => { | ||
global.window.location = { | ||
href: url, | ||
}; | ||
|
||
portal_url = portal_url || url; | ||
|
||
document.body.dataset.portalUrl = portal_url; | ||
}; | ||
|
||
it("navigationmarker roundtrip", () => { | ||
document.body.innerHTML = ` | ||
<nav class="pat-navigationmarker"> | ||
<ul> | ||
<li> | ||
<a href="/">Home</a> | ||
</li> | ||
<li> | ||
<a href="/path1">p1</a> | ||
</li> | ||
<li> | ||
<a href="/path2">p2</a> | ||
<ul> | ||
<li> | ||
<a href="/path2/path2.1">p2.1</a> | ||
</li> | ||
<li> | ||
<a href="/path2/path2.2">p2.2</a> | ||
<ul> | ||
<li> | ||
<a href="/path2/path2.2/path2.2.1">p2.2.1</a> | ||
</li> | ||
<li> | ||
<a href="/path2/path2.2/path2.2.2">p2.2.2</a> | ||
</li> | ||
</ul> | ||
</li> | ||
<li> | ||
<a href="../../path3">p1</a> | ||
</li> | ||
<li> | ||
<a href="https://patternslib.com/path4">p1</a> | ||
</li> | ||
</ul> | ||
</li> | ||
</ul> | ||
</nav> | ||
`; | ||
|
||
set_url("https://patternslib.com/"); | ||
|
||
const instance = new Pattern(document.querySelector(".pat-navigationmarker")); | ||
|
||
const it0 = document.querySelector("a[href='/']"); | ||
const it1 = document.querySelector("a[href='/path1']"); | ||
const it2 = document.querySelector("a[href='/path2']"); | ||
const it21 = document.querySelector("a[href='/path2/path2.1']"); | ||
const it22 = document.querySelector("a[href='/path2/path2.2']"); | ||
const it221 = document.querySelector("a[href='/path2/path2.2/path2.2.1']"); | ||
const it222 = document.querySelector("a[href='/path2/path2.2/path2.2.2']"); | ||
const it3 = document.querySelector("a[href='../../path3']"); | ||
const it4 = document.querySelector("a[href='https://patternslib.com/path4']"); | ||
|
||
expect(document.querySelectorAll(".current").length).toBe(1); | ||
expect(document.querySelectorAll(".inPath").length).toBe(0); | ||
expect(document.querySelector(".current a")).toBe(it0); | ||
|
||
instance.clear_items(); | ||
instance.mark_items("https://patternslib.com/path1"); | ||
|
||
expect(document.querySelectorAll(".current").length).toBe(1); | ||
expect(document.querySelectorAll(".inPath").length).toBe(0); | ||
expect(document.querySelector(".current a")).toBe(it1); | ||
|
||
instance.clear_items(); | ||
instance.mark_items("https://patternslib.com/path2"); | ||
|
||
expect(document.querySelectorAll(".current").length).toBe(1); | ||
expect(document.querySelectorAll(".inPath").length).toBe(0); | ||
expect(document.querySelector(".current a")).toBe(it2); | ||
|
||
instance.clear_items(); | ||
instance.mark_items("https://patternslib.com/path2/path2.1"); | ||
|
||
expect(document.querySelectorAll(".current").length).toBe(1); | ||
expect(document.querySelectorAll(".inPath").length).toBe(1); | ||
expect(document.querySelector(".current a")).toBe(it21); | ||
|
||
instance.clear_items(); | ||
instance.mark_items("https://patternslib.com/path2/path2.2"); | ||
|
||
expect(document.querySelectorAll(".current").length).toBe(1); | ||
expect(document.querySelectorAll(".inPath").length).toBe(1); | ||
expect(document.querySelector(".current a")).toBe(it22); | ||
|
||
instance.clear_items(); | ||
instance.mark_items("https://patternslib.com/path2/path2.2/path2.2.1"); | ||
|
||
expect(document.querySelectorAll(".current").length).toBe(1); | ||
expect(document.querySelectorAll(".inPath").length).toBe(2); | ||
expect(document.querySelector(".current a")).toBe(it221); | ||
|
||
instance.clear_items(); | ||
instance.mark_items("https://patternslib.com/path2/path2.2/path2.2.2"); | ||
|
||
expect(document.querySelectorAll(".current").length).toBe(1); | ||
expect(document.querySelectorAll(".inPath").length).toBe(2); | ||
expect(document.querySelector(".current a")).toBe(it222); | ||
|
||
instance.clear_items(); | ||
instance.mark_items("https://patternslib.com/path3"); | ||
|
||
expect(document.querySelectorAll(".current").length).toBe(1); | ||
expect(document.querySelectorAll(".inPath").length).toBe(0); | ||
expect(document.querySelector(".current a")).toBe(it3); | ||
|
||
instance.clear_items(); | ||
instance.mark_items("https://patternslib.com/path4"); | ||
|
||
expect(document.querySelectorAll(".current").length).toBe(1); | ||
expect(document.querySelectorAll(".inPath").length).toBe(0); | ||
expect(document.querySelector(".current a")).toBe(it4); | ||
}); | ||
}); |