@internetarchive/ia-item-navigator 1.0.4 → 1.1.1

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.
Files changed (59) hide show
  1. package/.github/workflows/ci.yml +4 -5
  2. package/.github/workflows/gh-pages-main.yml +39 -0
  3. package/.github/workflows/pr-preview.yml +38 -0
  4. package/demo/app-root.ts +13 -13
  5. package/dist/demo/app-root.d.ts +47 -47
  6. package/dist/demo/app-root.js +200 -199
  7. package/dist/demo/app-root.js.map +1 -1
  8. package/dist/index.d.ts +3 -3
  9. package/dist/index.js +3 -3
  10. package/dist/src/interfaces/custom-theater-interface.d.ts +20 -20
  11. package/dist/src/interfaces/custom-theater-interface.js +1 -1
  12. package/dist/src/interfaces/event-interfaces.d.ts +40 -40
  13. package/dist/src/interfaces/event-interfaces.js +1 -1
  14. package/dist/src/interfaces/menu-interfaces.d.ts +23 -22
  15. package/dist/src/interfaces/menu-interfaces.js +1 -1
  16. package/dist/src/interfaces/menu-interfaces.js.map +1 -1
  17. package/dist/src/item-navigator.d.ts +69 -69
  18. package/dist/src/item-navigator.js +259 -257
  19. package/dist/src/item-navigator.js.map +1 -1
  20. package/dist/src/loader.d.ts +9 -13
  21. package/dist/src/loader.js +35 -31
  22. package/dist/src/loader.js.map +1 -1
  23. package/dist/src/menu-slider/ia-menu-slider.d.ts +31 -30
  24. package/dist/src/menu-slider/ia-menu-slider.js +123 -124
  25. package/dist/src/menu-slider/ia-menu-slider.js.map +1 -1
  26. package/dist/src/menu-slider/menu-button.d.ts +19 -19
  27. package/dist/src/menu-slider/menu-button.js +75 -75
  28. package/dist/src/menu-slider/menu-button.js.map +1 -1
  29. package/dist/src/menu-slider/styles/menu-button.d.ts +2 -2
  30. package/dist/src/menu-slider/styles/menu-button.js +2 -2
  31. package/dist/src/menu-slider/styles/menu-slider.d.ts +2 -2
  32. package/dist/src/menu-slider/styles/menu-slider.js +5 -5
  33. package/dist/src/no-theater-available.d.ts +9 -9
  34. package/dist/src/no-theater-available.js +35 -35
  35. package/dist/src/no-theater-available.js.map +1 -1
  36. package/dist/test/ia-item-navigator.test.d.ts +1 -1
  37. package/dist/test/ia-item-navigator.test.js +296 -296
  38. package/dist/test/ia-item-navigator.test.js.map +1 -1
  39. package/dist/test/ia-stub.d.ts +22 -22
  40. package/dist/test/ia-stub.js +34 -34
  41. package/dist/test/no-theater-available.test.d.ts +1 -1
  42. package/dist/test/no-theater-available.test.js +22 -22
  43. package/dist/test/no-theater-available.test.js.map +1 -1
  44. package/dist/vite.config.d.ts +2 -0
  45. package/dist/vite.config.js +25 -0
  46. package/dist/vite.config.js.map +1 -0
  47. package/package.json +35 -21
  48. package/src/interfaces/menu-interfaces.ts +3 -1
  49. package/src/item-navigator.ts +9 -4
  50. package/src/loader.ts +5 -7
  51. package/src/menu-slider/ia-menu-slider.ts +17 -17
  52. package/src/menu-slider/menu-button.ts +3 -3
  53. package/src/no-theater-available.ts +1 -1
  54. package/test/ia-item-navigator.test.ts +24 -24
  55. package/test/iaux-item-navigator.test.txt +417 -0
  56. package/test/iaux-sharing-options.test.txt +84 -0
  57. package/test/no-theater-available.test.ts +2 -2
  58. package/tsconfig.json +2 -1
  59. package/vite.config.ts +25 -0
@@ -24,7 +24,7 @@ describe('ItemNavigator', () => {
24
24
  describe('Theaters', () => {
25
25
  it('shows <ia-no-theater-available> if told', async () => {
26
26
  const el = await fixture<ItemNavigator>(
27
- html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`
27
+ html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`,
28
28
  );
29
29
  el.viewAvailable = false;
30
30
  await el.updateComplete;
@@ -33,7 +33,7 @@ describe('ItemNavigator', () => {
33
33
  });
34
34
  it('opens main slot by default', async () => {
35
35
  const el = await fixture<ItemNavigator>(
36
- html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`
36
+ html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`,
37
37
  );
38
38
 
39
39
  expect(el.viewAvailable).to.be.true;
@@ -45,23 +45,23 @@ describe('ItemNavigator', () => {
45
45
  describe('`el.loaded`', () => {
46
46
  it('toggles the spinning loader', async () => {
47
47
  const el = await fixture<ItemNavigator>(
48
- html`<ia-item-navigator></ia-item-navigator>`
48
+ html`<ia-item-navigator></ia-item-navigator>`,
49
49
  );
50
50
  expect(el.loaded).to.be.null; // initial load
51
51
  expect(el.shadowRoot?.querySelector('ia-itemnav-loader')).to.exist;
52
52
  });
53
53
  it('hides reader section if `!loaded`', async () => {
54
54
  const el = await fixture<ItemNavigator>(
55
- html`<ia-item-navigator></ia-item-navigator>`
55
+ html`<ia-item-navigator></ia-item-navigator>`,
56
56
  );
57
57
 
58
58
  expect(
59
- el.shadowRoot?.querySelector('#reader')?.getAttribute('class')
59
+ el.shadowRoot?.querySelector('#reader')?.getAttribute('class'),
60
60
  ).to.contain('hidden');
61
61
  });
62
62
  it('shows reader when `loaded` ', async () => {
63
63
  const el = await fixture<ItemNavigator>(
64
- html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`
64
+ html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`,
65
65
  );
66
66
 
67
67
  el.loaded = true;
@@ -75,7 +75,7 @@ describe('ItemNavigator', () => {
75
75
  });
76
76
  it('listens to `@loadingStateUpdated` to update `loaded` for <no-theater-available>', async () => {
77
77
  const el = await fixture<ItemNavigator>(
78
- html`<ia-item-navigator></ia-item-navigator>`
78
+ html`<ia-item-navigator></ia-item-navigator>`,
79
79
  );
80
80
 
81
81
  await el.updateComplete;
@@ -100,7 +100,7 @@ describe('ItemNavigator', () => {
100
100
  const el = await fixture<ItemNavigator>(
101
101
  html`<ia-item-navigator
102
102
  .sharedObserver=${sharedObserver}
103
- ></ia-item-navigator>`
103
+ ></ia-item-navigator>`,
104
104
  );
105
105
 
106
106
  expect(el.sharedObserver).to.equal(sharedObserver);
@@ -113,7 +113,7 @@ describe('ItemNavigator', () => {
113
113
  await fixture<ItemNavigator>(
114
114
  html`<ia-item-navigator
115
115
  .sharedObserver=${sharedObserver}
116
- ></ia-item-navigator>`
116
+ ></ia-item-navigator>`,
117
117
  );
118
118
 
119
119
  expect(addObserverSpy.callCount).to.equal(2);
@@ -125,7 +125,7 @@ describe('ItemNavigator', () => {
125
125
  const el = await fixture<ItemNavigator>(
126
126
  html`<ia-item-navigator
127
127
  .sharedObserver=${sharedObserver}
128
- ></ia-item-navigator>`
128
+ ></ia-item-navigator>`,
129
129
  );
130
130
 
131
131
  el.disconnectedCallback();
@@ -135,7 +135,7 @@ describe('ItemNavigator', () => {
135
135
  });
136
136
  it('sets menu to overlay if container width is <= 600px', async () => {
137
137
  const el = await fixture<ItemNavigator>(
138
- html`<ia-item-navigator></ia-item-navigator>`
138
+ html`<ia-item-navigator></ia-item-navigator>`,
139
139
  );
140
140
 
141
141
  expect(el.openMenuState).to.equal('shift'); // as starting point
@@ -162,7 +162,7 @@ describe('ItemNavigator', () => {
162
162
  it('uses one', async () => {
163
163
  const modal = new ModalManager();
164
164
  const el = await fixture<ItemNavigator>(
165
- html`<ia-item-navigator .modal=${modal}></ia-item-navigator>`
165
+ html`<ia-item-navigator .modal=${modal}></ia-item-navigator>`,
166
166
  );
167
167
  expect(el.modal).to.equal(modal);
168
168
  });
@@ -172,7 +172,7 @@ describe('ItemNavigator', () => {
172
172
  it('creates reflected attribute `viewportinfullscreen`', async () => {
173
173
  /** to help with external styling adjustmnents */
174
174
  const el = await fixture<ItemNavigator>(
175
- html`<ia-item-navigator></ia-item-navigator>`
175
+ html`<ia-item-navigator></ia-item-navigator>`,
176
176
  );
177
177
  expect(el.getAttribute('viewportinfullscreen')).to.be.null;
178
178
 
@@ -183,7 +183,7 @@ describe('ItemNavigator', () => {
183
183
  });
184
184
  it('@ViewportInFullScreen', async () => {
185
185
  const el = await fixture<ItemNavigator>(
186
- html`<ia-item-navigator></ia-item-navigator>`
186
+ html`<ia-item-navigator></ia-item-navigator>`,
187
187
  );
188
188
  expect(el.viewportInFullscreen).to.be.null;
189
189
 
@@ -211,7 +211,7 @@ describe('ItemNavigator', () => {
211
211
  describe('el.menuOpened', () => {
212
212
  it('toggles side menu open', async () => {
213
213
  const el = await fixture<ItemNavigator>(
214
- html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`
214
+ html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`,
215
215
  );
216
216
 
217
217
  el.menuContents = [menuProvider];
@@ -223,7 +223,7 @@ describe('ItemNavigator', () => {
223
223
  // side menu starts closed
224
224
  expect(el.menuOpened).to.be.false;
225
225
  expect(nav?.querySelector('#menu')?.getAttribute('class')).to.contain(
226
- 'hidden'
226
+ 'hidden',
227
227
  );
228
228
 
229
229
  // let's open menu
@@ -232,13 +232,13 @@ describe('ItemNavigator', () => {
232
232
 
233
233
  expect(el.menuOpened).to.be.true;
234
234
  expect(nav?.querySelector('#menu')?.getAttribute('class')).to.not.contain(
235
- 'hidden'
235
+ 'hidden',
236
236
  );
237
237
  });
238
238
 
239
239
  it('opens menu shortcut with `@manageSideMenuEvents`', async () => {
240
240
  const el = await fixture<ItemNavigator>(
241
- html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`
241
+ html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`,
242
242
  );
243
243
  const detail = {
244
244
  menuId: 'fullscreen',
@@ -303,7 +303,7 @@ describe('ItemNavigator', () => {
303
303
  describe('el.menuContents', () => {
304
304
  it('draws side menu when populated', async () => {
305
305
  const el = await fixture<ItemNavigator>(
306
- html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`
306
+ html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`,
307
307
  );
308
308
 
309
309
  el.menuContents = [menuProvider];
@@ -324,7 +324,7 @@ describe('ItemNavigator', () => {
324
324
  describe('`el.menuShortcuts`', () => {
325
325
  it('displays shortcut & toggle side menu button', async () => {
326
326
  const el = await fixture<ItemNavigator>(
327
- html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`
327
+ html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`,
328
328
  );
329
329
 
330
330
  const anotherShortcut = {
@@ -350,7 +350,7 @@ describe('ItemNavigator', () => {
350
350
  describe('Menu events', () => {
351
351
  it('`el.setMenuShortcuts`', async () => {
352
352
  const el = await fixture<ItemNavigator>(
353
- html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`
353
+ html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`,
354
354
  );
355
355
  expect(el.menuShortcuts.length).to.equal(0);
356
356
 
@@ -365,7 +365,7 @@ describe('ItemNavigator', () => {
365
365
  });
366
366
  it('`el.setMenuContents`', async () => {
367
367
  const el = await fixture<ItemNavigator>(
368
- html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`
368
+ html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`,
369
369
  );
370
370
  expect(el.menuContents.length).to.equal(0);
371
371
 
@@ -378,7 +378,7 @@ describe('ItemNavigator', () => {
378
378
  });
379
379
  it('`el.setOpenMenu`', async () => {
380
380
  const el = await fixture<ItemNavigator>(
381
- html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`
381
+ html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`,
382
382
  );
383
383
 
384
384
  el.setOpenMenu({
@@ -400,7 +400,7 @@ describe('ItemNavigator', () => {
400
400
  });
401
401
  it('`el.closeMenu`', async () => {
402
402
  const el = await fixture<ItemNavigator>(
403
- html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`
403
+ html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`,
404
404
  );
405
405
 
406
406
  el.menuOpened = true;
@@ -0,0 +1,417 @@
1
+ /* eslint-disable camelcase */
2
+ import { html, fixture, expect } from '@open-wc/testing';
3
+ import Sinon from 'sinon';
4
+
5
+ import { SharedResizeObserver } from '@internetarchive/shared-resize-observer';
6
+ import { ModalManager } from '@internetarchive/modal-manager';
7
+ import { ItemNavigator } from '../src/iaux-item-navigator';
8
+ import '../src/iaux-item-navigator';
9
+
10
+ import { ItemStub, menuProvider, shortcut } from './ia-stub';
11
+ import {
12
+ ManageFullscreenEvent,
13
+ ToggleSideMenuOpenEvent,
14
+ SetSideMenuContentsEvent,
15
+ SetSideMenuShortcutsEvent,
16
+ ToggleSidePanelOpenEvent,
17
+ } from '../src/interfaces/event-interfaces';
18
+
19
+ afterEach(() => {
20
+ Sinon.restore();
21
+ });
22
+
23
+ describe('ItemNavigator', () => {
24
+ describe('Theaters', () => {
25
+ it('shows <ia-no-theater-available> if told', async () => {
26
+ const el = await fixture<ItemNavigator>(
27
+ html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`,
28
+ );
29
+ el.viewAvailable = false;
30
+ await el.updateComplete;
31
+ expect(el.viewAvailable).to.be.false;
32
+ expect(el.shadowRoot?.querySelector('ia-no-theater-available')).to.exist;
33
+ });
34
+ it('opens main slot by default', async () => {
35
+ const el = await fixture<ItemNavigator>(
36
+ html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`,
37
+ );
38
+
39
+ expect(el.viewAvailable).to.be.true;
40
+ expect(el.shadowRoot?.querySelector('ia-no-theater-available')).to.be
41
+ .null;
42
+ expect(el.shadowRoot?.querySelector('slot[name="main"]')).to.exist;
43
+ });
44
+ });
45
+ describe('`el.loaded`', () => {
46
+ it('toggles the spinning loader', async () => {
47
+ const el = await fixture<ItemNavigator>(
48
+ html`<ia-item-navigator></ia-item-navigator>`,
49
+ );
50
+ expect(el.loaded).to.be.null; // initial load
51
+ expect(el.shadowRoot?.querySelector('ia-itemnav-loader')).to.exist;
52
+ });
53
+ it('hides reader section if `!loaded`', async () => {
54
+ const el = await fixture<ItemNavigator>(
55
+ html`<ia-item-navigator></ia-item-navigator>`,
56
+ );
57
+
58
+ expect(
59
+ el.shadowRoot?.querySelector('#reader')?.getAttribute('class'),
60
+ ).to.contain('hidden');
61
+ });
62
+ it('shows reader when `loaded` ', async () => {
63
+ const el = await fixture<ItemNavigator>(
64
+ html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`,
65
+ );
66
+
67
+ el.loaded = true;
68
+ await el.updateComplete;
69
+ const mainTheaterSection = el.shadowRoot?.querySelector('#reader');
70
+ expect(mainTheaterSection?.classList.contains('hide')).to.be.false;
71
+ expect(el.loaded).to.be.true;
72
+ // `loaded` property is reflected as DOM attribute
73
+ expect(el.hasAttribute('loaded')).to.equal(true);
74
+ expect(el.shadowRoot?.querySelector('slot[name="main"]')).to.exist;
75
+ });
76
+ it('listens to `@loadingStateUpdated` to update `loaded` for <no-theater-available>', async () => {
77
+ const el = await fixture<ItemNavigator>(
78
+ html`<ia-item-navigator></ia-item-navigator>`,
79
+ );
80
+
81
+ await el.updateComplete;
82
+ const spy = Sinon.spy();
83
+ el.loadingStateUpdated = spy;
84
+ el.loaded = null;
85
+ el.viewAvailable = false;
86
+ await el.updateComplete;
87
+ // check base properties
88
+ expect(el.loaded).to.equal(null);
89
+ expect(el.item).to.be.undefined;
90
+
91
+ // spy fires
92
+ expect(spy.called).to.equal(true);
93
+ expect(spy.callCount).to.equal(1);
94
+ });
95
+ });
96
+
97
+ describe('`el.sharedObserver`', () => {
98
+ it('uses one', async () => {
99
+ const sharedObserver = new SharedResizeObserver();
100
+ const el = await fixture<ItemNavigator>(
101
+ html`<ia-item-navigator
102
+ .sharedObserver=${sharedObserver}
103
+ ></ia-item-navigator>`,
104
+ );
105
+
106
+ expect(el.sharedObserver).to.equal(sharedObserver);
107
+ expect(typeof el.handleResize).to.equal('function');
108
+ });
109
+ it('freshly registers handlers', async () => {
110
+ const sharedObserver = new SharedResizeObserver();
111
+ const addObserverSpy = Sinon.spy(sharedObserver, 'addObserver');
112
+
113
+ await fixture<ItemNavigator>(
114
+ html`<ia-item-navigator
115
+ .sharedObserver=${sharedObserver}
116
+ ></ia-item-navigator>`,
117
+ );
118
+
119
+ expect(addObserverSpy.callCount).to.equal(2);
120
+ });
121
+ it('removes handler when component disconnects', async () => {
122
+ const sharedObserver = new SharedResizeObserver();
123
+ const removeObserverSpy = Sinon.spy(sharedObserver, 'removeObserver');
124
+
125
+ const el = await fixture<ItemNavigator>(
126
+ html`<ia-item-navigator
127
+ .sharedObserver=${sharedObserver}
128
+ ></ia-item-navigator>`,
129
+ );
130
+
131
+ el.disconnectedCallback();
132
+ await el.updateComplete;
133
+
134
+ expect(removeObserverSpy.callCount).to.equal(1);
135
+ });
136
+ it('sets menu to overlay if container width is <= 600px', async () => {
137
+ const el = await fixture<ItemNavigator>(
138
+ html`<ia-item-navigator></ia-item-navigator>`,
139
+ );
140
+
141
+ expect(el.openMenuState).to.equal('shift'); // as starting point
142
+
143
+ const overlaySize = {
144
+ contentRect: { width: 600 },
145
+ } as ResizeObserverEntry;
146
+ el.handleResize(overlaySize);
147
+ await el.updateComplete;
148
+
149
+ expect(el.openMenuState).to.equal('overlay'); // changes open menu display to an overlay
150
+
151
+ const shiftSize = {
152
+ contentRect: { width: 601 },
153
+ } as ResizeObserverEntry;
154
+ el.handleResize(shiftSize);
155
+ await el.updateComplete;
156
+
157
+ expect(el.openMenuState).to.equal('shift');
158
+ });
159
+ });
160
+
161
+ describe('`el.modal`', () => {
162
+ it('uses one', async () => {
163
+ const modal = new ModalManager();
164
+ const el = await fixture<ItemNavigator>(
165
+ html`<ia-item-navigator .modal=${modal}></ia-item-navigator>`,
166
+ );
167
+ expect(el.modal).to.equal(modal);
168
+ });
169
+ });
170
+
171
+ describe('full browser window immersion "fullscreen"', () => {
172
+ it('creates reflected attribute `viewportinfullscreen`', async () => {
173
+ /** to help with external styling adjustmnents */
174
+ const el = await fixture<ItemNavigator>(
175
+ html`<ia-item-navigator></ia-item-navigator>`,
176
+ );
177
+ expect(el.getAttribute('viewportinfullscreen')).to.be.null;
178
+
179
+ el.viewportInFullscreen = true;
180
+ await el.updateComplete;
181
+
182
+ expect(el.getAttribute('viewportinfullscreen')).to.exist;
183
+ });
184
+ it('@ViewportInFullScreen', async () => {
185
+ const el = await fixture<ItemNavigator>(
186
+ html`<ia-item-navigator></ia-item-navigator>`,
187
+ );
188
+ expect(el.viewportInFullscreen).to.be.null;
189
+
190
+ const yesFullscreenEvent = {
191
+ detail: {
192
+ isFullScreen: true,
193
+ },
194
+ } as ManageFullscreenEvent;
195
+ el.manageViewportFullscreen(yesFullscreenEvent);
196
+ await el.updateComplete;
197
+ expect(el.viewportInFullscreen).to.be.true;
198
+
199
+ const noFullscreenEvent = {
200
+ detail: {
201
+ isFullScreen: false,
202
+ },
203
+ } as ManageFullscreenEvent;
204
+ el.manageViewportFullscreen(noFullscreenEvent);
205
+ await el.updateComplete;
206
+ expect(el.viewportInFullscreen).to.be.null;
207
+ });
208
+ });
209
+
210
+ /* Side menu & shortcuts tests */
211
+ describe('el.menuOpened', () => {
212
+ it('toggles side menu open', async () => {
213
+ const el = await fixture<ItemNavigator>(
214
+ html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`,
215
+ );
216
+
217
+ el.menuContents = [menuProvider];
218
+ await el.updateComplete;
219
+
220
+ const nav = el.shadowRoot?.querySelector('nav');
221
+
222
+ expect(nav?.querySelector('#menu')).to.exist;
223
+ // side menu starts closed
224
+ expect(el.menuOpened).to.be.false;
225
+ expect(nav?.querySelector('#menu')?.getAttribute('class')).to.contain(
226
+ 'hidden',
227
+ );
228
+
229
+ // let's open menu
230
+ el.toggleMenu();
231
+ await el.updateComplete;
232
+
233
+ expect(el.menuOpened).to.be.true;
234
+ expect(nav?.querySelector('#menu')?.getAttribute('class')).to.not.contain(
235
+ 'hidden',
236
+ );
237
+ });
238
+
239
+ it('opens menu shortcut with `@manageSideMenuEvents`', async () => {
240
+ const el = await fixture<ItemNavigator>(
241
+ html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`,
242
+ );
243
+ const detail = {
244
+ menuId: 'fullscreen',
245
+ action: 'open',
246
+ };
247
+
248
+ el.menuContents = [menuProvider];
249
+ await el.updateComplete;
250
+ const frame = el.shadowRoot?.querySelector('#frame');
251
+ // default menu open behavior is to side menu open, not overlay
252
+ expect(frame?.getAttribute('class')).to.contain('shift');
253
+
254
+ expect(el.menuOpened).to.be.false;
255
+ expect(el.openMenu).to.be.undefined;
256
+ expect(frame?.getAttribute('class')).to.not.contain('open');
257
+
258
+ const event = new CustomEvent('updateSideMenu', {
259
+ detail,
260
+ }) as ToggleSideMenuOpenEvent;
261
+ el.manageSideMenuEvents(event);
262
+ await el.updateComplete;
263
+
264
+ expect(el.shouldRenderMenu).to.be.true;
265
+ expect(el.menuOpened).to.be.true;
266
+ expect(el.openMenu).to.equal(detail.menuId);
267
+
268
+ expect(frame?.getAttribute('class')).to.contain('open');
269
+
270
+ // no menu provided
271
+ const openShortcutSpy = Sinon.spy(el, 'openShortcut');
272
+ const toggleMenuSpy = Sinon.spy(el, 'toggleMenu');
273
+
274
+ const noMenuProvidedEvent = new CustomEvent('updateSideMenu', {
275
+ detail: {},
276
+ }) as any;
277
+ el.manageSideMenuEvents(noMenuProvidedEvent);
278
+ await el.updateComplete;
279
+
280
+ expect(openShortcutSpy.called).to.be.false;
281
+ expect(toggleMenuSpy.called).to.be.false;
282
+
283
+ // toggle menu
284
+ const toggleMenuEvent = new CustomEvent('updateSideMenu', {
285
+ detail: { action: 'toggle', menuId: 'fullscreen' },
286
+ }) as any;
287
+ el.manageSideMenuEvents(toggleMenuEvent);
288
+ await el.updateComplete;
289
+
290
+ expect(toggleMenuSpy.callCount).to.equal(1);
291
+
292
+ // open menu
293
+ const openMenuEvent = new CustomEvent('updateSideMenu', {
294
+ detail: { action: 'open', menuId: 'fullscreen' },
295
+ }) as any;
296
+ el.manageSideMenuEvents(openMenuEvent);
297
+ await el.updateComplete;
298
+
299
+ expect(openShortcutSpy.callCount).to.equal(1);
300
+ });
301
+ });
302
+
303
+ describe('el.menuContents', () => {
304
+ it('draws side menu when populated', async () => {
305
+ const el = await fixture<ItemNavigator>(
306
+ html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`,
307
+ );
308
+
309
+ el.menuContents = [menuProvider];
310
+ await el.updateComplete;
311
+ expect(el.menuContents.length).to.exist;
312
+ expect(el.shouldRenderMenu).to.be.true;
313
+
314
+ const nav = el.shadowRoot?.querySelector('nav');
315
+ expect(nav).to.exist;
316
+
317
+ const menuSlider = nav?.querySelector('ia-menu-slider');
318
+ expect(menuSlider).to.exist;
319
+ expect(menuSlider?.getAttribute('manuallyhandleclose')).to.exist;
320
+ expect(menuSlider?.getAttribute('open')).to.exist;
321
+ });
322
+ });
323
+
324
+ describe('`el.menuShortcuts`', () => {
325
+ it('displays shortcut & toggle side menu button', async () => {
326
+ const el = await fixture<ItemNavigator>(
327
+ html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`,
328
+ );
329
+
330
+ const anotherShortcut = {
331
+ id: 'foo',
332
+ icon: html`<i class="foo-shortcut"></i>`,
333
+ };
334
+ el.menuContents = [menuProvider];
335
+ el.menuShortcuts = [shortcut, anotherShortcut];
336
+ await el.updateComplete;
337
+
338
+ const nav = el.shadowRoot?.querySelector('nav');
339
+
340
+ const shortcutsContainer = nav?.querySelector('.shortcuts');
341
+ expect(el.menuShortcuts.length).to.exist;
342
+ expect(nav).to.exist;
343
+ expect(shortcutsContainer).to.exist;
344
+ expect(shortcutsContainer?.querySelector('i.fullscreen-test')).to.exist;
345
+ expect(shortcutsContainer?.querySelector('button.shortcut.foo')).to.exist;
346
+ expect(nav?.querySelector('.toggle-menu')).to.exist;
347
+ });
348
+ });
349
+
350
+ describe('Menu events', () => {
351
+ it('`el.setMenuShortcuts`', async () => {
352
+ const el = await fixture<ItemNavigator>(
353
+ html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`,
354
+ );
355
+ expect(el.menuShortcuts.length).to.equal(0);
356
+
357
+ const menuShortcuts = [shortcut];
358
+
359
+ el.setMenuShortcuts({
360
+ detail: menuShortcuts,
361
+ } as SetSideMenuShortcutsEvent);
362
+ await el.updateComplete;
363
+
364
+ expect(el.menuShortcuts.length).to.equal(1);
365
+ });
366
+ it('`el.setMenuContents`', async () => {
367
+ const el = await fixture<ItemNavigator>(
368
+ html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`,
369
+ );
370
+ expect(el.menuContents.length).to.equal(0);
371
+
372
+ el.setMenuShortcuts({
373
+ detail: [menuProvider],
374
+ } as SetSideMenuContentsEvent);
375
+ await el.updateComplete;
376
+
377
+ expect(el.menuShortcuts.length).to.equal(1);
378
+ });
379
+ it('`el.setOpenMenu`', async () => {
380
+ const el = await fixture<ItemNavigator>(
381
+ html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`,
382
+ );
383
+
384
+ el.setOpenMenu({
385
+ detail: { id: 'foo' },
386
+ } as ToggleSidePanelOpenEvent);
387
+ await el.updateComplete;
388
+
389
+ expect(el.openMenu).to.equal('foo');
390
+ expect(el.selectedMenuId).to.equal('foo');
391
+
392
+ // toggles it off
393
+ el.setOpenMenu({
394
+ detail: { id: 'foo' },
395
+ } as ToggleSidePanelOpenEvent);
396
+ await el.updateComplete;
397
+
398
+ expect(el.openMenu).to.be.undefined;
399
+ expect(el.selectedMenuId).to.equal('');
400
+ });
401
+ it('`el.closeMenu`', async () => {
402
+ const el = await fixture<ItemNavigator>(
403
+ html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`,
404
+ );
405
+
406
+ el.menuOpened = true;
407
+ await el.updateComplete;
408
+
409
+ expect(el.menuOpened).to.be.true;
410
+
411
+ el.closeMenu();
412
+ await el.updateComplete;
413
+
414
+ expect(el.menuOpened).to.be.false;
415
+ });
416
+ });
417
+ });