@triptease/tt-navbar 0.0.35 → 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.35
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.35",
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,6 +63,8 @@ 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
  }
@@ -169,7 +172,7 @@ export class TtNavbar extends LitElement {
169
172
 
170
173
  private toggleSidebar = () => {
171
174
  this.closeAllDetails();
172
- this.sidebarOpen = !this.sidebarOpen;
175
+ this.navbarController.toggle();
173
176
  };
174
177
 
175
178
  private handleToggle = (e: ToggleEvent) => {
@@ -177,8 +180,8 @@ export class TtNavbar extends LitElement {
177
180
  const target = e.currentTarget as HTMLDetailsElement;
178
181
 
179
182
  if (newState === 'open') {
180
- if (!this.sidebarOpen) {
181
- this.sidebarOpen = true;
183
+ if (!this.navbarController.isOpen) {
184
+ this.navbarController.toggle();
182
185
  }
183
186
 
184
187
  this.closeAllDetails(target);
@@ -212,8 +215,8 @@ export class TtNavbar extends LitElement {
212
215
 
213
216
  render() {
214
217
  return html`
215
- <nav id=${this.id} class="${this.sidebarOpen ? '' : 'sidebar-closed'}">
216
- <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'}">
217
220
  <div class="logo">
218
221
  ${unsafeSVG(tripteaseLogo)}
219
222
  </div>
@@ -226,7 +229,7 @@ export class TtNavbar extends LitElement {
226
229
  </button>
227
230
  </div>
228
231
 
229
- <div class="nav-items ${this.sidebarOpen ? '' : 'sidebar-closed'}">
232
+ <div class="nav-items ${this.navbarController.isOpen ? '' : 'sidebar-closed'}">
230
233
  <a
231
234
  class="nav-item"
232
235
  href=${this.buildUrl('/')}
@@ -340,7 +343,7 @@ export class TtNavbar extends LitElement {
340
343
  </div>
341
344
  </details>
342
345
  </div>
343
- <div class="tertiary-nav ${this.sidebarOpen ? '' : 'sidebar-closed'}">
346
+ <div class="tertiary-nav ${this.navbarController.isOpen ? '' : 'sidebar-closed'}">
344
347
  <div id="external-links" class="nav-items">
345
348
  <a
346
349
  class="nav-item external-link"
@@ -373,23 +376,30 @@ export class TtNavbar extends LitElement {
373
376
  </div>
374
377
  <div class='nav-items'>
375
378
  <div id="client-selector">
376
- ${this.clients.length > 1 ? html`
377
- <tt-combobox
378
- .openUpward=${true}
379
- .value=${this.clientKey ? [this.clientKey] : []}
380
- @change=${this.handleClientChange}
381
- >
382
- ${this.clients.map((client) => html`
383
- <option slot="option" value=${client.clientKey}>
384
- ${client.displayName}
385
- </option>
386
- `)}
387
- </tt-combobox>
388
- ` : html`
389
- <div class="single-client-name">
390
- ${this.clients.find((m) => m.clientKey === this.clientKey)?.displayName}
391
- </div>
392
- `}
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
+ }
393
403
  </div>
394
404
  <a
395
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,26 +368,26 @@ 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
+ });
362
381
 
363
382
  it('should set the active route when receives `tetris:navigate` event', async () => {
364
383
  const navbar = await fixture<TtNavbar>(
365
- `<tt-navbar client-key=${CLIENT_KEY}></tt-navbar>`
384
+ `<tt-navbar client-key=${CLIENT_KEY}></tt-navbar>`,
366
385
  );
367
386
  // eslint-disable-next-line no-restricted-globals
368
- history.pushState({}, '', `/account/billing-management/${CLIENT_KEY}`)
369
-
387
+ history.pushState({}, '', `/account/billing-management/${CLIENT_KEY}`);
370
388
 
371
389
  // Dispatch the event to change to Channels route
372
- const navigateEvent = new CustomEvent('tetris:navigate', {
373
- });
390
+ const navigateEvent = new CustomEvent('tetris:navigate', {});
374
391
  window.dispatchEvent(navigateEvent);
375
392
 
376
393
  await elementUpdated(navbar);
@@ -378,6 +395,65 @@ describe('TtNavbar', () => {
378
395
 
379
396
  expect(anchor).to.exist;
380
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;
381
444
 
382
- })
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
+ });
383
459
  });