@triptease/tt-navbar 0.0.15 → 0.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "Webcomponent tt-navbar following open-wc recommendations",
4
4
  "license": "MIT",
5
5
  "author": "tt-navbar",
6
- "version": "0.0.15",
6
+ "version": "0.0.18",
7
7
  "type": "module",
8
8
  "main": "dist/src/index.js",
9
9
  "module": "dist/src/index.js",
package/src/TtNavbar.ts CHANGED
@@ -29,11 +29,66 @@ export class TtNavbar extends LitElement {
29
29
  @property({ type: String, attribute: 'client-key' })
30
30
  clientKey?: string;
31
31
 
32
+ @queryAll('details')
33
+ protected allDetailsElements!: Array<HTMLDetailsElement>;
34
+
35
+ @queryAll('a')
36
+ protected allNavLinks!: Array<HTMLAnchorElement>;
37
+
32
38
  @state()
33
39
  private sidebarOpen = true;
34
40
 
35
- @queryAll('details')
36
- protected allDetailsElements!: Array<HTMLDetailsElement>;
41
+ protected firstUpdated() {
42
+ this.setActiveState();
43
+ }
44
+
45
+ /*
46
+ * Set the active state for the current page.
47
+ *
48
+ * This function iterates over all nav links and compares the current URL path with the href of each link.
49
+ * If the current URL path starts with the href of a link, the link is marked as active. If multiple links
50
+ * share the same prefix, the longest prefix is used as it is more specific.
51
+ *
52
+ * Example:
53
+ * - Current URL: /channels/123
54
+ * - Nav links:
55
+ * - /channels
56
+ * - /channels/123
57
+ * - /channels/456
58
+ *
59
+ * Loop 1: currentPath = /channels/123, linkPath = /channels, bestMatch = /channels
60
+ * Loop 2: currentPath = /channels/123, linkPath = /channels/123, bestMatch = /channels/123
61
+ * Loop 3: currentPath = /channels/123, linkPath = /channels/456, bestMatch = /channels/123
62
+ * Result: /channels/123 is marked as active
63
+ *
64
+ */
65
+ private setActiveState = () => {
66
+ const currentPath = window.location.pathname;
67
+
68
+ let bestMatch: HTMLAnchorElement | undefined;
69
+ let bestMatchLength = 0;
70
+
71
+ for (const link of this.allNavLinks) {
72
+ link.classList.remove('current-page');
73
+ if (link.hasAttribute('aria-current')) {
74
+ link.attributes.removeNamedItem('aria-current');
75
+ }
76
+
77
+ const linkPath = new URL(link.href).pathname;
78
+
79
+ if (currentPath.startsWith(linkPath)) {
80
+ if (linkPath.length > bestMatchLength) {
81
+ bestMatch = link;
82
+ bestMatchLength = linkPath.length;
83
+ }
84
+ }
85
+ }
86
+
87
+ if (bestMatch) {
88
+ bestMatch.classList.add('current-page');
89
+ bestMatch.setAttribute('aria-current', 'page');
90
+ }
91
+ };
37
92
 
38
93
  private closeAllDetails = (element?: HTMLDetailsElement) => {
39
94
  this.allDetailsElements.forEach(detail => {
@@ -152,13 +207,13 @@ export class TtNavbar extends LitElement {
152
207
  >
153
208
  <a
154
209
  class="sub-nav-item"
155
- href=${this.buildUrl('/settings/group')}
210
+ href=${this.buildUrl('/settings/$CLIENT_KEY/guides')}
156
211
  @click=${this.onAnchorClick}
157
212
  >Group settings</a
158
213
  >
159
214
  <a
160
215
  class="sub-nav-item"
161
- href=${this.buildUrl('/settings/$CLIENT_KEY/hotels/')}
216
+ href=${this.buildUrl('/settings/$CLIENT_KEY/hotels')}
162
217
  @click=${this.onAnchorClick}
163
218
  >Property settings</a
164
219
  >
@@ -63,8 +63,8 @@ describe('TtNavbar', () => {
63
63
  expect(getLinkByHref(links, `/${CLIENT_KEY}/guest-behavioural-data`)).to
64
64
  .exist;
65
65
  expect(getLinkByHref(links, `/${CLIENT_KEY}/crm-config`)).to.exist;
66
- expect(getLinkByHref(links, `/settings/group`)).to.exist;
67
- expect(getLinkByHref(links, `/settings/${CLIENT_KEY}/hotels/`)).to.exist;
66
+ expect(getLinkByHref(links, `/settings/${CLIENT_KEY}/guides`)).to.exist;
67
+ expect(getLinkByHref(links, `/settings/${CLIENT_KEY}/hotels`)).to.exist;
68
68
  expect(getLinkByHref(links, `/account`)).to.exist;
69
69
  expect(getLinkByHref(links, `/account/team/${CLIENT_KEY}`)).to.exist;
70
70
  expect(getLinkByHref(links, `/account/billing-management/${CLIENT_KEY}`))
@@ -229,7 +229,33 @@ describe('TtNavbar', () => {
229
229
 
230
230
  element!.click();
231
231
 
232
- await waitUntil(() => logo?.checkVisibility(), "navbar should be open", { timeout: 500 });
232
+ await waitUntil(() => logo?.checkVisibility(), 'navbar should be open', {
233
+ timeout: 500,
234
+ });
235
+ });
236
+ });
237
+
238
+ const URLs = [
239
+ ['/', 'Dashboard'],
240
+ ['/channels', 'Channels'],
241
+ [`/parity/${CLIENT_KEY}`, 'Parity'],
242
+ [`/parity/${CLIENT_KEY}`, 'Parity'],
243
+ [`/parity/${CLIENT_KEY}/foo`, 'Parity'],
244
+ ];
245
+
246
+ URLs.forEach(([route, text]) => {
247
+ it(`should show the current page as active when link is ${route}`, async () => {
248
+ // eslint-disable-next-line no-restricted-globals
249
+ history.pushState({}, '', route); // 👈 mock URL
250
+
251
+ const navbar = await fixture<TtNavbar>(
252
+ `<tt-navbar client-key=${CLIENT_KEY}></tt-navbar>`,
253
+ );
254
+
255
+ const anchor = navbar.shadowRoot!.querySelector('a[aria-current="page"]');
256
+
257
+ expect(anchor).to.exist;
258
+ expect(anchor!.textContent).to.include(text);
233
259
  });
234
260
  });
235
261
  });