@internetarchive/ia-item-navigator 0.0.0-a7 → 0.0.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 (65) hide show
  1. package/README.md +29 -19
  2. package/demo/app-root.ts +70 -41
  3. package/demo/index.html +1 -2
  4. package/dist/demo/app-root.d.ts +20 -9
  5. package/dist/demo/app-root.js +61 -36
  6. package/dist/demo/app-root.js.map +1 -1
  7. package/dist/src/interfaces/custom-theater-interface.d.ts +20 -0
  8. package/dist/src/interfaces/custom-theater-interface.js +2 -0
  9. package/dist/src/interfaces/custom-theater-interface.js.map +1 -0
  10. package/dist/src/interfaces/event-interfaces.d.ts +11 -21
  11. package/dist/src/interfaces/event-interfaces.js.map +1 -1
  12. package/dist/src/interfaces/menu-interfaces.d.ts +6 -7
  13. package/dist/src/interfaces/menu-interfaces.js.map +1 -1
  14. package/dist/src/interfaces/nav-controller-interface.d.ts +11 -6
  15. package/dist/src/interfaces/nav-controller-interface.js.map +1 -1
  16. package/dist/src/item-inspector/item-inspector.d.ts +0 -41
  17. package/dist/src/item-inspector/item-inspector.js +253 -222
  18. package/dist/src/item-inspector/item-inspector.js.map +1 -1
  19. package/dist/src/item-inspector/visual-mod-provider.d.ts +2 -0
  20. package/dist/src/item-inspector/visual-mod-provider.js +3 -0
  21. package/dist/src/item-inspector/visual-mod-provider.js.map +1 -1
  22. package/dist/src/item-navigator.d.ts +45 -30
  23. package/dist/src/item-navigator.js +113 -120
  24. package/dist/src/item-navigator.js.map +1 -1
  25. package/dist/src/loader.d.ts +5 -0
  26. package/dist/src/loader.js +8 -2
  27. package/dist/src/loader.js.map +1 -1
  28. package/dist/src/no-theater-available.d.ts +9 -0
  29. package/dist/src/no-theater-available.js +79 -0
  30. package/dist/src/no-theater-available.js.map +1 -0
  31. package/dist/test/book-nav-stub.d.ts +22 -0
  32. package/dist/test/book-nav-stub.js +49 -0
  33. package/dist/test/book-nav-stub.js.map +1 -0
  34. package/dist/test/ia-item-navigator.test.d.ts +2 -0
  35. package/dist/test/ia-item-navigator.test.js +317 -0
  36. package/dist/test/ia-item-navigator.test.js.map +1 -0
  37. package/dist/test/ia-stub-goody.d.ts +210 -0
  38. package/dist/test/ia-stub-goody.js +276 -0
  39. package/dist/test/ia-stub-goody.js.map +1 -0
  40. package/dist/test/ia-stub.d.ts +22 -0
  41. package/dist/test/ia-stub.js +35 -0
  42. package/dist/test/ia-stub.js.map +1 -0
  43. package/dist/test/no-theater-available.test.d.ts +1 -0
  44. package/dist/test/no-theater-available.test.js +27 -0
  45. package/dist/test/no-theater-available.test.js.map +1 -0
  46. package/package.json +4 -3
  47. package/src/interfaces/custom-theater-interface.ts +28 -0
  48. package/src/interfaces/event-interfaces.ts +15 -22
  49. package/src/interfaces/menu-interfaces.ts +9 -10
  50. package/src/item-navigator.ts +154 -151
  51. package/src/loader.ts +9 -2
  52. package/src/no-theater-available.ts +85 -0
  53. package/test/book-nav-stub.ts +47 -0
  54. package/test/ia-item-navigator.test.ts +438 -0
  55. package/test/ia-stub.ts +79 -0
  56. package/test/no-theater-available.test.ts +32 -0
  57. package/demo/demo-book-manifest.json +0 -1163
  58. package/src/interfaces/nav-controller-interface.ts +0 -18
  59. package/src/item-inspector/files-by-type/files-by-type-provider.ts +0 -43
  60. package/src/item-inspector/files-by-type/ia-files-by-type.ts +0 -100
  61. package/src/item-inspector/item-inspector.ts +0 -242
  62. package/src/item-inspector/share-provider.ts +0 -51
  63. package/src/item-inspector/visual-mod-provider.ts +0 -60
  64. package/src/item-navigator-js.js +0 -372
  65. package/test/your-webcomponent.test.ts +0 -40
@@ -0,0 +1,85 @@
1
+ import {
2
+ LitElement,
3
+ customElement,
4
+ property,
5
+ html,
6
+ TemplateResult,
7
+ PropertyValues,
8
+ CSSResult,
9
+ css,
10
+ } from 'lit-element';
11
+
12
+ @customElement('ia-no-theater-available')
13
+ export class IANoTheaterAvailable extends LitElement {
14
+ @property({ type: String }) identifier?: string = '';
15
+
16
+ emitLoaded(): void {
17
+ this.dispatchEvent(
18
+ new CustomEvent<{ loaded: boolean }>('loadingStateUpdated', {
19
+ detail: { loaded: true },
20
+ })
21
+ );
22
+ }
23
+
24
+ updated(changed: PropertyValues): void {
25
+ if (changed.has('identifier')) {
26
+ this.emitLoaded();
27
+ }
28
+ }
29
+
30
+ get downloadUrl(): string {
31
+ return `/download/${this.identifier}`;
32
+ }
33
+
34
+ render(): TemplateResult {
35
+ return html`
36
+ <section>
37
+ <h2>THERE IS NO PREVIEW AVAILABLE FOR THIS ITEM</h2>
38
+ <p>
39
+ This item does not appear to have any files that can be experienced on
40
+ Archive.org. <br />
41
+ Please download files in this item to interact with them on your
42
+ computer.
43
+ </p>
44
+ <a href=${this.downloadUrl}>Show all files</a>
45
+ </section>
46
+ `;
47
+ }
48
+
49
+ static get styles(): CSSResult {
50
+ return css`
51
+ :host {
52
+ color: var(--primaryTextColor, #fff);
53
+ text-align: center;
54
+ }
55
+ section {
56
+ margin: 10% auto 0;
57
+ padding: 0 5%;
58
+ }
59
+ p {
60
+ font-size: 1.4rem;
61
+ }
62
+ a {
63
+ color: var(--primaryTextColor, #fff);
64
+ background-color: rgb(25, 72, 128);
65
+ min-height: 35px;
66
+ outline: none;
67
+ cursor: pointer;
68
+ line-height: normal;
69
+ border-radius: 0.4rem;
70
+ text-align: center;
71
+ vertical-align: middle;
72
+ font-size: 1.4rem;
73
+ font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
74
+ display: inline-block;
75
+ padding: 0.85rem 1.2rem;
76
+ border: 1px solid rgb(197, 209, 223);
77
+ white-space: nowrap;
78
+ appearance: auto;
79
+ box-sizing: border-box;
80
+ user-select: none;
81
+ text-decoration: none;
82
+ }
83
+ `;
84
+ }
85
+ }
@@ -0,0 +1,47 @@
1
+ import { ModalManager } from '@internetarchive/modal-manager';
2
+ import { SharedResizeObserver } from '@internetarchive/shared-resize-observer';
3
+ import { html, customElement, LitElement, property } from 'lit-element';
4
+ import { MetadataResponse } from '@internetarchive/search-service';
5
+ import {
6
+ MenuProviderInterface,
7
+ MenuShortcutInterface,
8
+ } from '../src/interfaces/menu-interfaces';
9
+ import { CustomTheaterInterface } from '../src/interfaces/custom-theater-interface';
10
+ @customElement('book-navigator')
11
+ export class BookNavigator
12
+ extends LitElement
13
+ implements CustomTheaterInterface {
14
+ @property({ attribute: false }) modal?: ModalManager;
15
+
16
+ @property({ type: Object }) itemMD?: MetadataResponse;
17
+
18
+ @property({ type: String }) baseHost?: string;
19
+
20
+ @property({ type: Boolean, reflect: true }) signedIn?: boolean | null = null;
21
+
22
+ @property({ type: Boolean }) sideMenuOpen!: boolean;
23
+
24
+ @property({ attribute: false }) sharedObserver?: SharedResizeObserver;
25
+
26
+ @property({ type: Array }) menuProviders?: MenuProviderInterface[];
27
+
28
+ @property({ type: Array }) menuShortcuts?: MenuShortcutInterface[];
29
+
30
+ emitLoadingStatusUpdate() {}
31
+
32
+ addMenuShortcut(menuId: string) {
33
+ return menuId;
34
+ }
35
+
36
+ removeMenuShortcut(menuId: string) {
37
+ return menuId;
38
+ }
39
+
40
+ sortMenuShortcuts() {}
41
+
42
+ emitMenuShortcutsUpdated() {}
43
+
44
+ render() {
45
+ return html` <p>foo</p> `;
46
+ }
47
+ }
@@ -0,0 +1,438 @@
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 { CustomTheaterInterface } from '../src/interfaces/custom-theater-interface';
8
+ import { ItemNavigator, ItemType } from '../src/item-navigator';
9
+ import '../src/item-navigator';
10
+
11
+ import '../test/book-nav-stub';
12
+ import { ItemStub, menuProvider, shortcut } from '../test/ia-stub';
13
+ import {
14
+ ManageFullscreenEvent,
15
+ ToggleSideMenuOpenEvent,
16
+ SetSideMenuContentsEvent,
17
+ SetSideMenuShortcutsEvent,
18
+ ToggleSidePanelOpenEvent,
19
+ } from '../src/interfaces/event-interfaces';
20
+
21
+ afterEach(() => {
22
+ Sinon.restore();
23
+ });
24
+
25
+ describe('ItemNavigator', () => {
26
+ describe('Theaters', () => {
27
+ it('shows <ia-no-theater-available> by default', async () => {
28
+ const el = await fixture<ItemNavigator>(
29
+ html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`
30
+ );
31
+ expect(el.shadowRoot?.querySelector('ia-no-theater-available')).to.exist;
32
+ });
33
+
34
+ it('shows <book-navigator> if `this.itemType = "bookreader"`', async () => {
35
+ const el = await fixture<ItemNavigator>(
36
+ html`<ia-item-navigator
37
+ .itemType=${ItemType.BOOK}
38
+ .item=${new ItemStub()}
39
+ .modal=${new ModalManager()}
40
+ .sharedObserver=${new SharedResizeObserver()}
41
+ ></ia-item-navigator>`
42
+ );
43
+
44
+ await el.updateComplete;
45
+
46
+ el.toggleMenu();
47
+ await el.updateComplete;
48
+
49
+ const bookNavigator = el.shadowRoot?.querySelector(
50
+ 'book-navigator'
51
+ ) as CustomTheaterInterface;
52
+ await bookNavigator.updateComplete;
53
+
54
+ console.log('132234234324324324');
55
+ // TODO: add BookNavigator type & import via @internetarchive/bookreader
56
+ // For now, let's check that the BookNavigator element and its properties exist w/ stub
57
+ expect(bookNavigator).to.exist;
58
+ expect(bookNavigator?.modal).to.exist;
59
+ expect(bookNavigator?.baseHost).to.exist;
60
+ expect(bookNavigator?.signedIn).to.be.null;
61
+ expect(bookNavigator?.sharedObserver).to.exist;
62
+ expect(bookNavigator?.sideMenuOpen).to.exist;
63
+ });
64
+ });
65
+ describe('`el.loaded`', () => {
66
+ it('toggles the spinning loader', async () => {
67
+ const el = await fixture<ItemNavigator>(
68
+ html`<ia-item-navigator></ia-item-navigator>`
69
+ );
70
+ expect(el.loaded).to.be.null; // initial load
71
+ expect(el.shadowRoot?.querySelector('ia-itemnav-loader')).to.exist;
72
+ });
73
+ it('hides reader section if `!loaded`', async () => {
74
+ const el = await fixture<ItemNavigator>(
75
+ html`<ia-item-navigator></ia-item-navigator>`
76
+ );
77
+
78
+ expect(
79
+ el.shadowRoot?.querySelector('#reader')?.getAttribute('class')
80
+ ).to.contain('hide');
81
+ });
82
+ it('shows reader when `loaded` ', async () => {
83
+ const el = await fixture<ItemNavigator>(
84
+ html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`
85
+ );
86
+
87
+ const mainTheaterSection = el.shadowRoot?.querySelector('#reader');
88
+ expect(mainTheaterSection?.classList.contains('hide')).to.be.false;
89
+ expect(el.loaded).to.be.true;
90
+ // `loaded` property is reflected as DOM attribute
91
+ expect(el.hasAttribute('loaded')).to.equal(true);
92
+ expect(el.shadowRoot?.querySelector('ia-no-theater-available')).to.exist;
93
+ });
94
+ it('listens to `@loadingStateUpdated` to update `loaded`', async () => {
95
+ const el = await fixture<ItemNavigator>(
96
+ html`<ia-item-navigator></ia-item-navigator>`
97
+ );
98
+
99
+ await el.updateComplete;
100
+ const spy = Sinon.spy();
101
+ el.loadingStateUpdated = spy;
102
+ el.loaded = null;
103
+ await el.updateComplete;
104
+ // check base properties
105
+ expect(el.loaded).to.equal(null);
106
+ expect(el.item).to.be.undefined;
107
+
108
+ // hydrate item
109
+ el.item = new ItemStub();
110
+ await el.updateComplete;
111
+
112
+ // spy fires
113
+ expect(spy.called).to.equal(true);
114
+ expect(spy.callCount).to.equal(1);
115
+ });
116
+ });
117
+
118
+ describe('`el.sharedObserver`', () => {
119
+ it('uses one', async () => {
120
+ const sharedObserver = new SharedResizeObserver();
121
+ const el = await fixture<ItemNavigator>(
122
+ html`<ia-item-navigator
123
+ .sharedObserver=${sharedObserver}
124
+ ></ia-item-navigator>`
125
+ );
126
+
127
+ expect(el.sharedObserver).to.equal(sharedObserver);
128
+ expect(typeof el.handleResize).to.equal('function');
129
+ });
130
+ it('freshly registers handler', async () => {
131
+ const sharedObserver = new SharedResizeObserver();
132
+ const addObserverSpy = Sinon.spy(sharedObserver, 'addObserver');
133
+
134
+ await fixture<ItemNavigator>(
135
+ html`<ia-item-navigator
136
+ .sharedObserver=${sharedObserver}
137
+ ></ia-item-navigator>`
138
+ );
139
+
140
+ expect(addObserverSpy.callCount).to.equal(1);
141
+ });
142
+ it('removes handler when component disconnects', async () => {
143
+ const sharedObserver = new SharedResizeObserver();
144
+ const removeObserverSpy = Sinon.spy(sharedObserver, 'removeObserver');
145
+
146
+ const el = await fixture<ItemNavigator>(
147
+ html`<ia-item-navigator
148
+ .sharedObserver=${sharedObserver}
149
+ ></ia-item-navigator>`
150
+ );
151
+
152
+ el.disconnectedCallback();
153
+ await el.updateComplete;
154
+
155
+ expect(removeObserverSpy.callCount).to.equal(1);
156
+ });
157
+ it('sets menu to overlay if container width is <= 600px', async () => {
158
+ const el = await fixture<ItemNavigator>(
159
+ html`<ia-item-navigator></ia-item-navigator>`
160
+ );
161
+
162
+ expect(el.openMenuState).to.equal('shift'); // as starting point
163
+
164
+ const overlaySize = {
165
+ contentRect: { width: 600 },
166
+ } as ResizeObserverEntry;
167
+ el.handleResize(overlaySize);
168
+ await el.updateComplete;
169
+
170
+ expect(el.openMenuState).to.equal('overlay'); // changes open menu display to an overlay
171
+
172
+ const shiftSize = {
173
+ contentRect: { width: 601 },
174
+ } as ResizeObserverEntry;
175
+ el.handleResize(shiftSize);
176
+ await el.updateComplete;
177
+
178
+ expect(el.openMenuState).to.equal('shift');
179
+ });
180
+ });
181
+
182
+ describe('`el.modal`', () => {
183
+ it('uses one', async () => {
184
+ const modal = new ModalManager();
185
+ const el = await fixture<ItemNavigator>(
186
+ html`<ia-item-navigator .modal=${modal}></ia-item-navigator>`
187
+ );
188
+ expect(el.modal).to.equal(modal);
189
+ });
190
+ });
191
+
192
+ describe('full browser window immersion "fullscreen"', () => {
193
+ it('creates reflected attribute `viewportinfullscreen`', async () => {
194
+ /** to help with external styling adjustmnents */
195
+ const el = await fixture<ItemNavigator>(
196
+ html`<ia-item-navigator></ia-item-navigator>`
197
+ );
198
+ expect(el.getAttribute('viewportinfullscreen')).to.be.null;
199
+
200
+ el.viewportInFullscreen = true;
201
+ await el.updateComplete;
202
+
203
+ expect(el.getAttribute('viewportinfullscreen')).to.exist;
204
+ });
205
+ it('@ViewportInFullScreen', async () => {
206
+ const el = await fixture<ItemNavigator>(
207
+ html`<ia-item-navigator></ia-item-navigator>`
208
+ );
209
+ expect(el.viewportInFullscreen).to.be.null;
210
+
211
+ const yesFullscreenEvent = {
212
+ detail: {
213
+ isFullScreen: true,
214
+ },
215
+ } as ManageFullscreenEvent;
216
+ el.manageViewportFullscreen(yesFullscreenEvent);
217
+ await el.updateComplete;
218
+ expect(el.viewportInFullscreen).to.be.true;
219
+
220
+ const noFullscreenEvent = {
221
+ detail: {
222
+ isFullScreen: false,
223
+ },
224
+ } as ManageFullscreenEvent;
225
+ el.manageViewportFullscreen(noFullscreenEvent);
226
+ await el.updateComplete;
227
+ expect(el.viewportInFullscreen).to.be.null;
228
+ });
229
+ });
230
+
231
+ /* Side menu & shortcuts tests */
232
+ describe('el.menuOpened', () => {
233
+ it('toggles side menu open', async () => {
234
+ const el = await fixture<ItemNavigator>(
235
+ html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`
236
+ );
237
+
238
+ el.menuContents = [menuProvider];
239
+ await el.updateComplete;
240
+
241
+ const nav = el.shadowRoot?.querySelector('nav');
242
+
243
+ expect(nav?.querySelector('#menu')).to.exist;
244
+ // side menu starts closed
245
+ expect(el.menuOpened).to.be.false;
246
+ expect(nav?.querySelector('#menu')?.getAttribute('class')).to.contain(
247
+ 'hidden'
248
+ );
249
+
250
+ // let's open menu
251
+ el.toggleMenu();
252
+ await el.updateComplete;
253
+
254
+ expect(el.menuOpened).to.be.true;
255
+ expect(nav?.querySelector('#menu')?.getAttribute('class')).to.not.contain(
256
+ 'hidden'
257
+ );
258
+ });
259
+
260
+ it('opens menu shortcut with `@manageSideMenuEvents`', async () => {
261
+ const el = await fixture<ItemNavigator>(
262
+ html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`
263
+ );
264
+ const detail = {
265
+ menuId: 'fullscreen',
266
+ action: 'open',
267
+ };
268
+
269
+ el.menuContents = [menuProvider];
270
+ await el.updateComplete;
271
+ const frame = el.shadowRoot?.querySelector('#frame');
272
+ // default menu open behavior is to side menu open, not overlay
273
+ expect(frame?.getAttribute('class')).to.contain('shift');
274
+
275
+ expect(el.menuOpened).to.be.false;
276
+ expect(el.openMenu).to.be.undefined;
277
+ expect(frame?.getAttribute('class')).to.not.contain('open');
278
+
279
+ const event = new CustomEvent('updateSideMenu', {
280
+ detail,
281
+ }) as ToggleSideMenuOpenEvent;
282
+ el.manageSideMenuEvents(event);
283
+ await el.updateComplete;
284
+
285
+ expect(el.shouldRenderMenu).to.be.true;
286
+ expect(el.menuOpened).to.be.true;
287
+ expect(el.openMenu).to.equal(detail.menuId);
288
+
289
+ expect(frame?.getAttribute('class')).to.contain('open');
290
+
291
+ // no menu provided
292
+ const openShortcutSpy = Sinon.spy(el, 'openShortcut');
293
+ const toggleMenuSpy = Sinon.spy(el, 'toggleMenu');
294
+
295
+ const noMenuProvidedEvent = new CustomEvent('updateSideMenu', {
296
+ detail: {},
297
+ }) as any;
298
+ el.manageSideMenuEvents(noMenuProvidedEvent);
299
+ await el.updateComplete;
300
+
301
+ expect(openShortcutSpy.called).to.be.false;
302
+ expect(toggleMenuSpy.called).to.be.false;
303
+
304
+ // toggle menu
305
+ const toggleMenuEvent = new CustomEvent('updateSideMenu', {
306
+ detail: { action: 'toggle', menuId: 'fullscreen' },
307
+ }) as any;
308
+ el.manageSideMenuEvents(toggleMenuEvent);
309
+ await el.updateComplete;
310
+
311
+ expect(toggleMenuSpy.callCount).to.equal(1);
312
+
313
+ // open menu
314
+ const openMenuEvent = new CustomEvent('updateSideMenu', {
315
+ detail: { action: 'open', menuId: 'fullscreen' },
316
+ }) as any;
317
+ el.manageSideMenuEvents(openMenuEvent);
318
+ await el.updateComplete;
319
+
320
+ expect(openShortcutSpy.callCount).to.equal(1);
321
+ });
322
+ });
323
+
324
+ describe('el.menuContents', () => {
325
+ it('draws side menu when populated', async () => {
326
+ const el = await fixture<ItemNavigator>(
327
+ html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`
328
+ );
329
+
330
+ el.menuContents = [menuProvider];
331
+ await el.updateComplete;
332
+ expect(el.menuContents.length).to.exist;
333
+ expect(el.shouldRenderMenu).to.be.true;
334
+
335
+ const nav = el.shadowRoot?.querySelector('nav');
336
+ expect(nav).to.exist;
337
+
338
+ const menuSlider = nav?.querySelector('ia-menu-slider');
339
+ expect(menuSlider).to.exist;
340
+ expect(menuSlider?.getAttribute('manuallyhandleclose')).to.exist;
341
+ expect(menuSlider?.getAttribute('open')).to.exist;
342
+ });
343
+ });
344
+
345
+ describe('`el.menuShortcuts`', () => {
346
+ it('displays shortcut & toggle side menu button', async () => {
347
+ const el = await fixture<ItemNavigator>(
348
+ html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`
349
+ );
350
+
351
+ const anotherShortcut = {
352
+ id: 'foo',
353
+ icon: html`<i class="foo-shortcut"></i>`,
354
+ };
355
+ el.menuContents = [menuProvider];
356
+ el.menuShortcuts = [shortcut, anotherShortcut];
357
+ await el.updateComplete;
358
+
359
+ const nav = el.shadowRoot?.querySelector('nav');
360
+
361
+ const shortcutsContainer = nav?.querySelector('.shortcuts');
362
+ expect(el.menuShortcuts.length).to.exist;
363
+ expect(nav).to.exist;
364
+ expect(shortcutsContainer).to.exist;
365
+ expect(shortcutsContainer?.querySelector('i.fullscreen-test')).to.exist;
366
+ expect(shortcutsContainer?.querySelector('button.shortcut.foo')).to.exist;
367
+ expect(nav?.querySelector('.toggle-menu')).to.exist;
368
+ });
369
+ });
370
+
371
+ describe('Menu events', () => {
372
+ it('`el.setMenuShortcuts`', async () => {
373
+ const el = await fixture<ItemNavigator>(
374
+ html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`
375
+ );
376
+ expect(el.menuShortcuts.length).to.equal(0);
377
+
378
+ const menuShortcuts = [shortcut];
379
+
380
+ el.setMenuShortcuts({
381
+ detail: menuShortcuts,
382
+ } as SetSideMenuShortcutsEvent);
383
+ await el.updateComplete;
384
+
385
+ expect(el.menuShortcuts.length).to.equal(1);
386
+ });
387
+ it('`el.setMenuContents`', async () => {
388
+ const el = await fixture<ItemNavigator>(
389
+ html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`
390
+ );
391
+ expect(el.menuContents.length).to.equal(0);
392
+
393
+ el.setMenuShortcuts({
394
+ detail: [menuProvider],
395
+ } as SetSideMenuContentsEvent);
396
+ await el.updateComplete;
397
+
398
+ expect(el.menuShortcuts.length).to.equal(1);
399
+ });
400
+ it('`el.setOpenMenu`', async () => {
401
+ const el = await fixture<ItemNavigator>(
402
+ html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`
403
+ );
404
+
405
+ el.setOpenMenu({
406
+ detail: { id: 'foo' },
407
+ } as ToggleSidePanelOpenEvent);
408
+ await el.updateComplete;
409
+
410
+ expect(el.openMenu).to.equal('foo');
411
+ expect(el.selectedMenuId).to.equal('foo');
412
+
413
+ // toggles it off
414
+ el.setOpenMenu({
415
+ detail: { id: 'foo' },
416
+ } as ToggleSidePanelOpenEvent);
417
+ await el.updateComplete;
418
+
419
+ expect(el.openMenu).to.be.undefined;
420
+ expect(el.selectedMenuId).to.equal('');
421
+ });
422
+ it('`el.closeMenu`', async () => {
423
+ const el = await fixture<ItemNavigator>(
424
+ html`<ia-item-navigator .item=${new ItemStub()}></ia-item-navigator>`
425
+ );
426
+
427
+ el.menuOpened = true;
428
+ await el.updateComplete;
429
+
430
+ expect(el.menuOpened).to.be.true;
431
+
432
+ el.closeMenu();
433
+ await el.updateComplete;
434
+
435
+ expect(el.menuOpened).to.be.false;
436
+ });
437
+ });
438
+ });
@@ -0,0 +1,79 @@
1
+ /* eslint-disable camelcase */
2
+ import {
3
+ MetadataResponse,
4
+ Metadata,
5
+ File,
6
+ Review,
7
+ SpeechMusicASREntry,
8
+ } from '@internetarchive/search-service';
9
+ import { html } from 'lit-html';
10
+ import {
11
+ MenuShortcutInterface,
12
+ MenuProviderInterface,
13
+ } from '../src/interfaces/menu-interfaces';
14
+
15
+ export class ItemStub implements MetadataResponse {
16
+ constructor() {
17
+ this.rawResponse = '';
18
+ this.created = 1;
19
+ this.d1 = 'hello';
20
+ this.d2 = 'boop';
21
+ this.dir = 'whee';
22
+ this.files = [];
23
+ this.files_count = 0;
24
+ this.item_last_updated = 2020;
25
+ this.item_size = 111;
26
+ this.metadata = { identifier: 'foo' } as Metadata;
27
+ this.server = 'foo-server';
28
+ this.uniq = 2;
29
+ this.workable_servers = ['abc'];
30
+ }
31
+
32
+ rawResponse: any;
33
+
34
+ created: number;
35
+
36
+ d1: string;
37
+
38
+ d2: string;
39
+
40
+ dir: string;
41
+
42
+ files: File[];
43
+
44
+ files_count: number;
45
+
46
+ item_last_updated: number;
47
+
48
+ item_size: number;
49
+
50
+ metadata: Metadata;
51
+
52
+ server: string;
53
+
54
+ uniq: number;
55
+
56
+ workable_servers: string[];
57
+
58
+ speech_vs_music_asr?: SpeechMusicASREntry[] | undefined;
59
+
60
+ reviews?: Review[] | undefined;
61
+ }
62
+
63
+ export const shortcut = {
64
+ id: 'fullscreen',
65
+ icon: html`<i class="fas fullscreen-test"></i>`,
66
+ } as MenuShortcutInterface;
67
+
68
+ export const menuProvider = {
69
+ ...shortcut,
70
+ label: 'foo',
71
+ menuDetails: html`<div>foo</div>`,
72
+ selected: true,
73
+ followable: false,
74
+ href: 'https://archive.foo',
75
+ item: new ItemStub(),
76
+ baseHost: 'https://archive.foo',
77
+ subPrefix: 'bar',
78
+ updated: () => {},
79
+ } as MenuProviderInterface;
@@ -0,0 +1,32 @@
1
+ import { html, fixture, expect } from '@open-wc/testing';
2
+ import { IANoTheaterAvailable } from '../src/no-theater-available';
3
+ import '../src/no-theater-available';
4
+
5
+ describe('IANoTheaterAvailable', () => {
6
+ it('Fires `loadingStateUpdated` on identifier change', async () => {
7
+ const el = await fixture<IANoTheaterAvailable>(
8
+ html`<ia-no-theater-available
9
+ .identifier=${`foo`}
10
+ ></ia-no-theater-available>`
11
+ );
12
+ let eventFired = false;
13
+ el.addEventListener('loadingStateUpdated', () => {
14
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
15
+ eventFired = true;
16
+ });
17
+ expect(eventFired).to.be.false;
18
+
19
+ el.identifier = 'bar';
20
+ await el.updateComplete;
21
+ expect(eventFired).to.be.true;
22
+ });
23
+ it('Has link to item download page', async () => {
24
+ const el = await fixture<IANoTheaterAvailable>(
25
+ html`<ia-no-theater-available
26
+ .identifier=${`foo`}
27
+ ></ia-no-theater-available>`
28
+ );
29
+ expect(el.downloadUrl).to.equal('/download/foo');
30
+ expect(el?.shadowRoot?.querySelector('a')?.href).to.contain(el.downloadUrl);
31
+ });
32
+ });