@triptease/tt-navbar 0.0.34 → 0.0.36

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.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @triptease/tt-navbar v0.0.34
2
+ * @triptease/tt-navbar v0.0.36
3
3
  */
4
4
 
5
5
  // src/urlMappings.ts
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.34",
6
+ "version": "0.0.36",
7
7
  "type": "module",
8
8
  "main": "dist/src/index.js",
9
9
  "module": "dist/src/index.js",
@@ -27,6 +27,7 @@
27
27
  "storybook:build": "tsc && npm run analyze -- --exclude dist && storybook build"
28
28
  },
29
29
  "dependencies": {
30
+ "@clerk/clerk-js": "^5.102.1",
30
31
  "@triptease/icons": "^1.1.1",
31
32
  "@triptease/tt-combobox": "^5.3.0",
32
33
  "lit": "^3.1.4"
@@ -0,0 +1,40 @@
1
+ import { ReactiveController, ReactiveControllerHost } from 'lit';
2
+
3
+ export class NavbarController implements ReactiveController {
4
+ host: ReactiveControllerHost;
5
+
6
+ private _isOpen = true;
7
+
8
+ constructor(host: ReactiveControllerHost) {
9
+ (this.host = host).addController(this);
10
+ }
11
+
12
+ get isOpen() {
13
+ return this._isOpen;
14
+ }
15
+
16
+ hostConnected() {
17
+ if (window.Clerk) {
18
+ const userSetting =
19
+ window.Clerk.user?.unsafeMetadata?.preferences?.ui?.navigationOpen;
20
+
21
+ if (userSetting !== undefined) {
22
+ this._isOpen = userSetting;
23
+ this.host.requestUpdate();
24
+ }
25
+ }
26
+ }
27
+
28
+ async toggle() {
29
+ const newState = !this._isOpen;
30
+ this._isOpen = newState;
31
+
32
+ if (window.Clerk) {
33
+ await window.Clerk.user?.update({
34
+ unsafeMetadata: { preferences: { ui: { navigationOpen: newState } } },
35
+ });
36
+ }
37
+
38
+ this.host.requestUpdate();
39
+ }
40
+ }
package/src/TtNavbar.ts CHANGED
@@ -12,13 +12,14 @@ import {
12
12
  logout,
13
13
  sidebarCollapsed,
14
14
  user,
15
- wallet
15
+ wallet,
16
16
  } from '@triptease/icons';
17
17
  import '@triptease/tt-combobox';
18
18
  import { styles } from './styles.js';
19
19
  import { tripteaseLogo } from './triptease-logo.js';
20
20
  import { urlMappings } from './urlMappings.js';
21
21
  import { Routes } from './Routes.js';
22
+ import { NavbarController } from './NavbarController.js';
22
23
 
23
24
  const jsonArrayConverter = (value: string | null) => {
24
25
  if (!value) return [];
@@ -51,7 +52,7 @@ export class TtNavbar extends LitElement {
51
52
  clients: { clientKey: string; displayName: string }[] = [];
52
53
 
53
54
  @property({ type: Object })
54
- onClientChange: ((clientKeySelected: string) => void ) | undefined;
55
+ onClientChange: ((clientKeySelected: string) => void) | undefined;
55
56
 
56
57
  @queryAll('details')
57
58
  protected allDetailsElements!: Array<HTMLDetailsElement>;
@@ -62,19 +63,26 @@ export class TtNavbar extends LitElement {
62
63
  @state()
63
64
  private sidebarOpen = true;
64
65
 
66
+ private navbarController = new NavbarController(this);
67
+
65
68
  protected firstUpdated() {
66
69
  this.setActiveState();
67
70
  }
68
71
 
69
72
  public connectedCallback() {
70
-
73
+ // Listen for browser back/forward navigation
71
74
  window.addEventListener('popstate', this.setActiveState);
75
+
76
+ // Listen for Tetris programmatic navigation
77
+ window.addEventListener('tetris:navigate', this.setActiveState);
78
+
72
79
  super.connectedCallback();
73
80
  }
74
81
 
75
82
  public disconnectedCallback() {
76
-
77
83
  window.removeEventListener('popstate', this.setActiveState);
84
+ window.removeEventListener('tetris:navigate', this.setActiveState);
85
+
78
86
  super.disconnectedCallback();
79
87
  }
80
88
 
@@ -164,7 +172,7 @@ export class TtNavbar extends LitElement {
164
172
 
165
173
  private toggleSidebar = () => {
166
174
  this.closeAllDetails();
167
- this.sidebarOpen = !this.sidebarOpen;
175
+ this.navbarController.toggle();
168
176
  };
169
177
 
170
178
  private handleToggle = (e: ToggleEvent) => {
@@ -172,8 +180,8 @@ export class TtNavbar extends LitElement {
172
180
  const target = e.currentTarget as HTMLDetailsElement;
173
181
 
174
182
  if (newState === 'open') {
175
- if (!this.sidebarOpen) {
176
- this.sidebarOpen = true;
183
+ if (!this.navbarController.isOpen) {
184
+ this.navbarController.toggle();
177
185
  }
178
186
 
179
187
  this.closeAllDetails(target);
@@ -207,8 +215,8 @@ export class TtNavbar extends LitElement {
207
215
 
208
216
  render() {
209
217
  return html`
210
- <nav id=${this.id} class="${this.sidebarOpen ? '' : 'sidebar-closed'}">
211
- <div class="sidebar-header ${this.sidebarOpen ? '' : 'sidebar-closed'}">
218
+ <nav id=${this.id} class="${this.navbarController.isOpen ? '' : 'sidebar-closed'}">
219
+ <div class="sidebar-header ${this.navbarController.isOpen ? '' : 'sidebar-closed'}">
212
220
  <div class="logo">
213
221
  ${unsafeSVG(tripteaseLogo)}
214
222
  </div>
@@ -221,7 +229,7 @@ export class TtNavbar extends LitElement {
221
229
  </button>
222
230
  </div>
223
231
 
224
- <div class="nav-items ${this.sidebarOpen ? '' : 'sidebar-closed'}">
232
+ <div class="nav-items ${this.navbarController.isOpen ? '' : 'sidebar-closed'}">
225
233
  <a
226
234
  class="nav-item"
227
235
  href=${this.buildUrl('/')}
@@ -335,7 +343,7 @@ export class TtNavbar extends LitElement {
335
343
  </div>
336
344
  </details>
337
345
  </div>
338
- <div class="tertiary-nav ${this.sidebarOpen ? '' : 'sidebar-closed'}">
346
+ <div class="tertiary-nav ${this.navbarController.isOpen ? '' : 'sidebar-closed'}">
339
347
  <div id="external-links" class="nav-items">
340
348
  <a
341
349
  class="nav-item external-link"
@@ -368,23 +376,30 @@ export class TtNavbar extends LitElement {
368
376
  </div>
369
377
  <div class='nav-items'>
370
378
  <div id="client-selector">
371
- ${this.clients.length > 1 ? html`
372
- <tt-combobox
373
- .openUpward=${true}
374
- .value=${this.clientKey ? [this.clientKey] : []}
375
- @change=${this.handleClientChange}
376
- >
377
- ${this.clients.map((client) => html`
378
- <option slot="option" value=${client.clientKey}>
379
- ${client.displayName}
380
- </option>
381
- `)}
382
- </tt-combobox>
383
- ` : html`
384
- <div class="single-client-name">
385
- ${this.clients.find((m) => m.clientKey === this.clientKey)?.displayName}
386
- </div>
387
- `}
379
+ ${
380
+ this.clients.length > 1
381
+ ? html`
382
+ <tt-combobox
383
+ .openUpward=${true}
384
+ .value=${this.clientKey ? [this.clientKey] : []}
385
+ @change=${this.handleClientChange}
386
+ >
387
+ ${this.clients.map(
388
+ client => html`
389
+ <option slot="option" value=${client.clientKey}>
390
+ ${client.displayName}
391
+ </option>
392
+ `,
393
+ )}
394
+ </tt-combobox>
395
+ `
396
+ : html`
397
+ <div class="single-client-name">
398
+ ${this.clients.find(m => m.clientKey === this.clientKey)
399
+ ?.displayName}
400
+ </div>
401
+ `
402
+ }
388
403
  </div>
389
404
  <a
390
405
  id="logout-link"
@@ -0,0 +1,16 @@
1
+ import { Clerk } from '@clerk/clerk-js';
2
+
3
+ declare global {
4
+ interface Window {
5
+ Clerk?: typeof Clerk;
6
+ }
7
+
8
+ interface UserUnsafeMetadata {
9
+ preferences: {
10
+ ui?: {
11
+ navigationVersion?: 2;
12
+ navigationOpen?: boolean;
13
+ };
14
+ };
15
+ }
16
+ }
@@ -1,5 +1,12 @@
1
+ import { Clerk } from '@clerk/clerk-js';
1
2
  import '../src/tt-navbar.js';
2
- import { elementUpdated, expect, fixture, nextFrame, waitUntil } from '@open-wc/testing';
3
+ import {
4
+ elementUpdated,
5
+ expect,
6
+ fixture,
7
+ nextFrame,
8
+ waitUntil,
9
+ } from '@open-wc/testing';
3
10
  import { TtNavbar } from '../src/index.js';
4
11
  import { Routes } from '../src/Routes.js';
5
12
 
@@ -15,12 +22,16 @@ const getLinkByHref = (links: NodeListOf<HTMLAnchorElement>, href: string) => {
15
22
  };
16
23
 
17
24
  const selectComboboxOption = async (combobox: Element, value: string) => {
18
- const input = combobox.shadowRoot!.querySelector('[role="combobox"]') as HTMLInputElement;
25
+ const input = combobox.shadowRoot!.querySelector(
26
+ '[role="combobox"]',
27
+ ) as HTMLInputElement;
19
28
  input.click();
20
29
 
21
30
  await elementUpdated(combobox);
22
31
 
23
- const option = combobox.shadowRoot!.querySelector(`[role="option"][data-value="${value}"]`) as HTMLLIElement;
32
+ const option = combobox.shadowRoot!.querySelector(
33
+ `[role="option"][data-value="${value}"]`,
34
+ ) as HTMLLIElement;
24
35
  option.click();
25
36
  };
26
37
 
@@ -29,15 +40,15 @@ const isVisuallyHidden = (element: Element) => {
29
40
 
30
41
  return Boolean(
31
42
  style.position === 'absolute' &&
32
- style.width === '1px' &&
33
- style.height === '1px' &&
34
- style.margin === '-1px' &&
35
- style.padding === '0px' &&
36
- style.borderWidth === '0px' &&
37
- style.overflow === 'hidden' &&
38
- style.clip === 'rect(0px, 0px, 0px, 0px)' &&
39
- style.clipPath === 'inset(50%)' &&
40
- style.whiteSpace === 'nowrap'
43
+ style.width === '1px' &&
44
+ style.height === '1px' &&
45
+ style.margin === '-1px' &&
46
+ style.padding === '0px' &&
47
+ style.borderWidth === '0px' &&
48
+ style.overflow === 'hidden' &&
49
+ style.clip === 'rect(0px, 0px, 0px, 0px)' &&
50
+ style.clipPath === 'inset(50%)' &&
51
+ style.whiteSpace === 'nowrap',
41
52
  );
42
53
  };
43
54
 
@@ -53,7 +64,7 @@ describe('TtNavbar', () => {
53
64
  });
54
65
  it('should render with the default links', async () => {
55
66
  const navbar = await fixture<TtNavbar>(
56
- `<tt-navbar client-key=${CLIENT_KEY}></tt-navbar>`
67
+ `<tt-navbar client-key=${CLIENT_KEY}></tt-navbar>`,
57
68
  );
58
69
  const links = navbar.shadowRoot?.querySelectorAll('a');
59
70
 
@@ -80,7 +91,7 @@ describe('TtNavbar', () => {
80
91
  it('should render platform URLs against the base URL when it is defined', async () => {
81
92
  const platformUrl = 'https://app.triptease.io';
82
93
  const navbar = await fixture<TtNavbar>(
83
- `<tt-navbar client-key=${CLIENT_KEY} platform-url="${platformUrl}"></tt-navbar>`
94
+ `<tt-navbar client-key=${CLIENT_KEY} platform-url="${platformUrl}"></tt-navbar>`,
84
95
  );
85
96
  const links = navbar.shadowRoot?.querySelectorAll('a');
86
97
 
@@ -88,7 +99,8 @@ describe('TtNavbar', () => {
88
99
  expect(getLinkByHref(links, `${platformUrl}/`)).to.exist;
89
100
  expect(getLinkByHref(links, 'https://app.campaign-manager.triptease.io'))
90
101
  .to.exist; // This shouldn't change
91
- expect(getLinkByHref(links, `${platformUrl}/${CLIENT_KEY}/channels`)).to.exist;
102
+ expect(getLinkByHref(links, `${platformUrl}/${CLIENT_KEY}/channels`)).to
103
+ .exist;
92
104
  }
93
105
  });
94
106
 
@@ -101,7 +113,7 @@ describe('TtNavbar', () => {
101
113
  };
102
114
 
103
115
  const navbar = await fixture<TtNavbar>(
104
- `<tt-navbar client-key=${CLIENT_KEY}></tt-navbar>`
116
+ `<tt-navbar client-key=${CLIENT_KEY}></tt-navbar>`,
105
117
  );
106
118
  navbar.navigate = onNavigate;
107
119
  await navbar.updateComplete;
@@ -111,22 +123,24 @@ describe('TtNavbar', () => {
111
123
 
112
124
  await waitUntil(
113
125
  () => expect(navigateEventCount).to.equal(13),
114
- 'navigate event did not fire'
126
+ 'navigate event did not fire',
115
127
  );
116
128
  });
117
129
 
118
130
  it('should render a combobox when multiple clients are provided', async () => {
119
131
  const navbar = await fixture<TtNavbar>(
120
- `<tt-navbar client-key=${CLIENT_KEY}></tt-navbar>`
132
+ `<tt-navbar client-key=${CLIENT_KEY}></tt-navbar>`,
121
133
  );
122
134
  navbar.clients = [
123
135
  { clientKey: CLIENT_KEY, displayName: 'Client One' },
124
- { clientKey: 'abc123', displayName: 'Client Two' }
136
+ { clientKey: 'abc123', displayName: 'Client Two' },
125
137
  ];
126
138
  await navbar.updateComplete;
127
139
 
128
140
  const combobox = navbar.shadowRoot?.querySelector('tt-combobox');
129
- const singleClientName = navbar.shadowRoot?.querySelector('.single-client-name');
141
+ const singleClientName = navbar.shadowRoot?.querySelector(
142
+ '.single-client-name',
143
+ );
130
144
 
131
145
  expect(combobox).to.exist;
132
146
  expect(singleClientName).to.not.exist;
@@ -135,14 +149,14 @@ describe('TtNavbar', () => {
135
149
 
136
150
  it('should render the client name when only one client is provided', async () => {
137
151
  const navbar = await fixture<TtNavbar>(
138
- `<tt-navbar client-key=${CLIENT_KEY}></tt-navbar>`
152
+ `<tt-navbar client-key=${CLIENT_KEY}></tt-navbar>`,
139
153
  );
140
- navbar.clients = [
141
- { clientKey: CLIENT_KEY, displayName: 'Single Client' }
142
- ];
154
+ navbar.clients = [{ clientKey: CLIENT_KEY, displayName: 'Single Client' }];
143
155
  await navbar.updateComplete;
144
156
 
145
- const singleClientName = navbar.shadowRoot?.querySelector('.single-client-name');
157
+ const singleClientName = navbar.shadowRoot?.querySelector(
158
+ '.single-client-name',
159
+ );
146
160
  const combobox = navbar.shadowRoot?.querySelector('tt-combobox');
147
161
 
148
162
  expect(singleClientName).to.exist;
@@ -152,7 +166,7 @@ describe('TtNavbar', () => {
152
166
 
153
167
  it('should render the logout link', async () => {
154
168
  const navbar = await fixture<TtNavbar>(
155
- `<tt-navbar client-key=${CLIENT_KEY}></tt-navbar>`
169
+ `<tt-navbar client-key=${CLIENT_KEY}></tt-navbar>`,
156
170
  );
157
171
 
158
172
  const links = navbar.shadowRoot?.querySelectorAll('a');
@@ -163,7 +177,7 @@ describe('TtNavbar', () => {
163
177
 
164
178
  it('should close other details when one is being opened', async () => {
165
179
  const navbar = await fixture<TtNavbar>(
166
- `<tt-navbar client-key=${CLIENT_KEY}></tt-navbar>`
180
+ `<tt-navbar client-key=${CLIENT_KEY}></tt-navbar>`,
167
181
  );
168
182
 
169
183
  const allDetails = navbar.shadowRoot!.querySelectorAll('summary');
@@ -189,11 +203,11 @@ describe('TtNavbar', () => {
189
203
 
190
204
  it('should collapse the navbar when toggle clicked', async () => {
191
205
  const navbar = await fixture<TtNavbar>(
192
- `<tt-navbar client-key=${CLIENT_KEY}></tt-navbar>`
206
+ `<tt-navbar client-key=${CLIENT_KEY}></tt-navbar>`,
193
207
  );
194
208
 
195
209
  const navbarToggleBtn = navbar.shadowRoot!.querySelector(
196
- '#navbar-toggle-btn'
210
+ '#navbar-toggle-btn',
197
211
  ) as HTMLButtonElement;
198
212
 
199
213
  navbarToggleBtn.click();
@@ -223,7 +237,7 @@ describe('TtNavbar', () => {
223
237
  for (const element of [
224
238
  ...rootLinksHiddenElements,
225
239
  ...summaryHiddenElements,
226
- logoutLink!
240
+ logoutLink!,
227
241
  ]) {
228
242
  expect(isVisuallyHidden(element)).to.be.true;
229
243
  }
@@ -233,16 +247,16 @@ describe('TtNavbar', () => {
233
247
  'market-insights',
234
248
  'settings',
235
249
  'account',
236
- 'billing-routes'
250
+ 'billing-routes',
237
251
  ];
238
252
  collapsibleIds.forEach(id => {
239
253
  it(`should open the nav bar when the ${id} collapsible is clicked`, async () => {
240
254
  const navbar = await fixture<TtNavbar>(
241
- `<tt-navbar client-key=${CLIENT_KEY}></tt-navbar>`
255
+ `<tt-navbar client-key=${CLIENT_KEY}></tt-navbar>`,
242
256
  );
243
257
 
244
258
  const navbarToggleBtn = navbar.shadowRoot!.querySelector(
245
- '#navbar-toggle-btn'
259
+ '#navbar-toggle-btn',
246
260
  ) as HTMLButtonElement;
247
261
 
248
262
  navbarToggleBtn.click();
@@ -259,7 +273,7 @@ describe('TtNavbar', () => {
259
273
  element!.click();
260
274
 
261
275
  await waitUntil(() => logo?.checkVisibility(), 'navbar should be open', {
262
- timeout: 500
276
+ timeout: 500,
263
277
  });
264
278
  });
265
279
  });
@@ -272,7 +286,7 @@ describe('TtNavbar', () => {
272
286
  [`/parity/${CLIENT_KEY}/foo`, 'Parity'],
273
287
  [`/meta/${CLIENT_KEY}/performance`, 'Channels'],
274
288
  [`/chat/insights/${CLIENT_KEY}`, 'Channels'],
275
- [`/${CLIENT_KEY}/email`, 'Channels']
289
+ [`/${CLIENT_KEY}/email`, 'Channels'],
276
290
  ];
277
291
 
278
292
  URLs.forEach(([route, text]) => {
@@ -281,7 +295,7 @@ describe('TtNavbar', () => {
281
295
  history.pushState({}, '', route); // 👈 mock URL
282
296
 
283
297
  const navbar = await fixture<TtNavbar>(
284
- `<tt-navbar client-key=${CLIENT_KEY}></tt-navbar>`
298
+ `<tt-navbar client-key=${CLIENT_KEY}></tt-navbar>`,
285
299
  );
286
300
 
287
301
  const anchor = navbar.shadowRoot!.querySelector('a[aria-current="page"]');
@@ -296,7 +310,7 @@ describe('TtNavbar', () => {
296
310
  const campaignManagerUrl = 'http://localhost:8000';
297
311
 
298
312
  const navbar = await fixture<TtNavbar>(
299
- `<tt-navbar client-key=${CLIENT_KEY} platform-url=${platformUrl} campaign-manager-url=${campaignManagerUrl} active-route="${Routes.CampaignManager}"></tt-navbar>`
313
+ `<tt-navbar client-key=${CLIENT_KEY} platform-url=${platformUrl} campaign-manager-url=${campaignManagerUrl} active-route="${Routes.CampaignManager}"></tt-navbar>`,
300
314
  );
301
315
  const anchor = navbar.shadowRoot!.querySelector('a[aria-current="page"]');
302
316
  expect(anchor).to.exist;
@@ -310,11 +324,11 @@ describe('TtNavbar', () => {
310
324
  };
311
325
 
312
326
  const navbar = await fixture<TtNavbar>(
313
- `<tt-navbar client-key=${CLIENT_KEY}></tt-navbar>`
327
+ `<tt-navbar client-key=${CLIENT_KEY}></tt-navbar>`,
314
328
  );
315
329
  navbar.clients = [
316
330
  { clientKey: CLIENT_KEY, displayName: 'Client One' },
317
- { clientKey: 'abc123', displayName: 'Client Two' }
331
+ { clientKey: 'abc123', displayName: 'Client Two' },
318
332
  ];
319
333
  navbar.onClientChange = onClientChange;
320
334
 
@@ -330,20 +344,23 @@ describe('TtNavbar', () => {
330
344
 
331
345
  it('should set the --nav-bar-width css variable when collapsed', async () => {
332
346
  const navbar = await fixture<TtNavbar>(
333
- `<tt-navbar client-key=${CLIENT_KEY}></tt-navbar>`
347
+ `<tt-navbar client-key=${CLIENT_KEY}></tt-navbar>`,
334
348
  );
335
349
 
336
- await expect(getComputedStyle(navbar).getPropertyValue('--nav-bar-width').trim()).to.equal('260px');
350
+ await expect(
351
+ getComputedStyle(navbar).getPropertyValue('--nav-bar-width').trim(),
352
+ ).to.equal('260px');
337
353
 
338
354
  const navbarToggleBtn = navbar.shadowRoot!.querySelector(
339
- '#navbar-toggle-btn'
355
+ '#navbar-toggle-btn',
340
356
  ) as HTMLButtonElement;
341
357
 
342
358
  navbarToggleBtn.click();
343
359
  await elementUpdated(navbar);
344
360
 
345
- await expect(getComputedStyle(navbar).getPropertyValue('--nav-bar-width').trim()).to.equal('fit-content');
346
-
361
+ await expect(
362
+ getComputedStyle(navbar).getPropertyValue('--nav-bar-width').trim(),
363
+ ).to.equal('fit-content');
347
364
  });
348
365
 
349
366
  it('should expand the details when the current page is within that section', async () => {
@@ -351,12 +368,92 @@ describe('TtNavbar', () => {
351
368
  history.pushState({}, '', `/parity/${CLIENT_KEY}`);
352
369
 
353
370
  const navbar = await fixture<TtNavbar>(
354
- `<tt-navbar client-key=${CLIENT_KEY}></tt-navbar>`
371
+ `<tt-navbar client-key=${CLIENT_KEY}></tt-navbar>`,
355
372
  );
356
373
 
357
- const marketInsightsDetails = navbar.shadowRoot!.querySelector('#market-insights') as HTMLDetailsElement;
374
+ const marketInsightsDetails = navbar.shadowRoot!.querySelector(
375
+ '#market-insights',
376
+ ) as HTMLDetailsElement;
358
377
 
359
378
  expect(marketInsightsDetails).to.exist;
360
379
  expect(marketInsightsDetails.open).to.be.true;
361
- })
380
+ });
381
+
382
+ it('should set the active route when receives `tetris:navigate` event', async () => {
383
+ const navbar = await fixture<TtNavbar>(
384
+ `<tt-navbar client-key=${CLIENT_KEY}></tt-navbar>`,
385
+ );
386
+ // eslint-disable-next-line no-restricted-globals
387
+ history.pushState({}, '', `/account/billing-management/${CLIENT_KEY}`);
388
+
389
+ // Dispatch the event to change to Channels route
390
+ const navigateEvent = new CustomEvent('tetris:navigate', {});
391
+ window.dispatchEvent(navigateEvent);
392
+
393
+ await elementUpdated(navbar);
394
+ const anchor = navbar.shadowRoot!.querySelector('a[aria-current="page"]');
395
+
396
+ expect(anchor).to.exist;
397
+ expect(anchor!.textContent).to.include('Booking reconciliation');
398
+ });
399
+
400
+ it('should default to open when Clerk is not available', async () => {
401
+ const navbar = await fixture<TtNavbar>(
402
+ `<tt-navbar client-key=${CLIENT_KEY}></tt-navbar>`,
403
+ );
404
+
405
+ const logo = navbar.shadowRoot!.querySelector('.logo');
406
+
407
+ expect(window.Clerk).to.be.undefined;
408
+ expect(logo).to.exist;
409
+ expect(logo!.checkVisibility()).to.be.true;
410
+ });
411
+
412
+ it('should default to state stored in Clerk when set', async () => {
413
+ window.Clerk = {
414
+ user: {
415
+ unsafeMetadata: { preferences: { ui: { navigationOpen: false } } },
416
+ },
417
+ } as Clerk;
418
+
419
+ const navbar = await fixture<TtNavbar>(
420
+ `<tt-navbar client-key=${CLIENT_KEY}></tt-navbar>`,
421
+ );
422
+
423
+ const logo = navbar.shadowRoot!.querySelector('.logo');
424
+
425
+ expect(logo).to.exist;
426
+ expect(logo!.checkVisibility()).to.be.false;
427
+
428
+ window.Clerk = undefined; // Cleanup
429
+ });
430
+
431
+ it('should update Clerk state when toggling if Clerk is available', async () => {
432
+ let timesCalled = 0;
433
+
434
+ const mockUpdate = () => {
435
+ timesCalled += 1;
436
+ };
437
+
438
+ window.Clerk = {
439
+ user: {
440
+ update: mockUpdate,
441
+ unsafeMetadata: { preferences: { ui: { navigationOpen: false } } },
442
+ },
443
+ } as unknown as Clerk;
444
+
445
+ const navbar = await fixture<TtNavbar>(
446
+ `<tt-navbar client-key=${CLIENT_KEY}></tt-navbar>`,
447
+ );
448
+
449
+ const navbarToggleBtn = navbar.shadowRoot!.querySelector(
450
+ '#navbar-toggle-btn',
451
+ ) as HTMLButtonElement;
452
+
453
+ navbarToggleBtn.click();
454
+
455
+ await expect(timesCalled).to.equal(1);
456
+
457
+ window.Clerk = undefined; // Cleanup
458
+ });
362
459
  });