@internetarchive/ia-item-navigator 0.0.0-a8 → 0.0.2-a1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (62) hide show
  1. package/README.md +29 -19
  2. package/demo/app-root.ts +50 -50
  3. package/demo/index.html +1 -0
  4. package/dist/demo/app-root.d.ts +19 -15
  5. package/dist/demo/app-root.js +36 -39
  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 -11
  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 -47
  17. package/dist/src/item-inspector/item-inspector.js +253 -270
  18. package/dist/src/item-inspector/item-inspector.js.map +1 -1
  19. package/dist/src/item-navigator.d.ts +45 -28
  20. package/dist/src/item-navigator.js +122 -115
  21. package/dist/src/item-navigator.js.map +1 -1
  22. package/dist/src/loader.d.ts +5 -0
  23. package/dist/src/loader.js +8 -2
  24. package/dist/src/loader.js.map +1 -1
  25. package/dist/src/no-theater-available.d.ts +9 -0
  26. package/dist/src/no-theater-available.js +79 -0
  27. package/dist/src/no-theater-available.js.map +1 -0
  28. package/dist/test/book-nav-stub.d.ts +22 -0
  29. package/dist/test/book-nav-stub.js +49 -0
  30. package/dist/test/book-nav-stub.js.map +1 -0
  31. package/dist/test/ia-item-navigator.test.d.ts +2 -0
  32. package/dist/test/ia-item-navigator.test.js +317 -0
  33. package/dist/test/ia-item-navigator.test.js.map +1 -0
  34. package/dist/test/ia-stub-goody.d.ts +210 -0
  35. package/dist/test/ia-stub-goody.js +276 -0
  36. package/dist/test/ia-stub-goody.js.map +1 -0
  37. package/dist/test/ia-stub.d.ts +22 -0
  38. package/dist/test/ia-stub.js +35 -0
  39. package/dist/test/ia-stub.js.map +1 -0
  40. package/dist/test/no-theater-available.test.d.ts +1 -0
  41. package/dist/test/no-theater-available.test.js +27 -0
  42. package/dist/test/no-theater-available.test.js.map +1 -0
  43. package/package.json +4 -3
  44. package/src/interfaces/custom-theater-interface.ts +28 -0
  45. package/src/interfaces/event-interfaces.ts +15 -11
  46. package/src/interfaces/menu-interfaces.ts +9 -10
  47. package/src/item-navigator.ts +165 -145
  48. package/src/loader.ts +9 -2
  49. package/src/no-theater-available.ts +85 -0
  50. package/test/book-nav-stub.ts +47 -0
  51. package/test/ia-item-navigator.test.ts +438 -0
  52. package/test/ia-stub.ts +79 -0
  53. package/test/no-theater-available.test.ts +32 -0
  54. package/demo/demo-book-manifest.json +0 -1163
  55. package/src/interfaces/nav-controller-interface.ts +0 -18
  56. package/src/item-inspector/files-by-type/files-by-type-provider.ts +0 -43
  57. package/src/item-inspector/files-by-type/ia-files-by-type.ts +0 -100
  58. package/src/item-inspector/item-inspector.ts +0 -295
  59. package/src/item-inspector/share-provider.ts +0 -51
  60. package/src/item-inspector/visual-mod-provider.ts +0 -65
  61. package/src/item-navigator-js.js +0 -372
  62. 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
+ });