@internetarchive/ia-item-navigator 1.1.0 → 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.
@@ -76,7 +76,7 @@ let IaMenuSlider = class IaMenuSlider extends LitElement {
76
76
  .icon=${menu.icon}
77
77
  .label=${menu.label}
78
78
  .menuDetails=${menu.menuDetails}
79
- .id=${menu.id}
79
+ .buttonId=${menu.id}
80
80
  .selected=${menu.id === this.selectedMenu}
81
81
  .followable=${menu.followable}
82
82
  .href=${menu.href}
@@ -1 +1 @@
1
- {"version":3,"file":"ia-menu-slider.js","sourceRoot":"","sources":["../../../src/menu-slider/ia-menu-slider.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitElement, html, nothing } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport menuSliderCSS from './styles/menu-slider';\nimport '@internetarchive/icon-collapse-sidebar';\nimport './menu-button';\nimport { MenuProviderInterface } from '../interfaces/menu-interfaces';\n\nconst sliderEvents = {\n closeDrawer: 'menuSliderClosed',\n};\n\n@customElement('ia-menu-slider')\nexport class IaMenuSlider extends LitElement {\n static get styles() {\n return menuSliderCSS;\n }\n\n @property({ type: Array }) menus: MenuProviderInterface[] = [];\n\n @property({ type: Boolean }) open = false;\n\n @property({ type: Boolean }) manuallyHandleClose = false;\n\n @property({ type: String }) selectedMenu = '';\n\n @property({ type: Object }) selectedMenuAction = nothing;\n\n @property({ type: Boolean }) animateMenuOpen = false;\n\n updated() {\n const { actionButton } =\n this.selectedMenuDetails || ({} as Record<string, any>);\n const actionButtonHasChanged = actionButton !== this.selectedMenuAction;\n if (actionButtonHasChanged) {\n this.selectedMenuAction = actionButton || nothing;\n }\n }\n\n /**\n * Event handler, captures state of selected menu\n */\n setSelectedMenu({ detail }: CustomEvent) {\n const { id } = detail;\n this.selectedMenu = this.selectedMenu === id ? '' : id;\n const { actionButton } =\n this.selectedMenuDetails || ({} as Record<string, any>);\n this.selectedMenuAction = actionButton || nothing;\n }\n\n /**\n * closes menu drawer\n */\n closeMenu() {\n if (!this.manuallyHandleClose) {\n this.open = false;\n }\n const { closeDrawer } = sliderEvents;\n const drawerClosed = new CustomEvent(closeDrawer, {\n detail: this.selectedMenuDetails,\n });\n this.dispatchEvent(drawerClosed);\n }\n\n get selectedMenuDetails() {\n const selectedMenu = this.menus.find(\n menu => (menu as any).id === this.selectedMenu,\n );\n return selectedMenu;\n }\n\n get selectedMenuComponent() {\n const menuItem = this.selectedMenuDetails;\n return menuItem && (menuItem as any)?.component\n ? (menuItem as any).component\n : html``;\n }\n\n /* render */\n\n get sliderDetailsClass() {\n const animate = this.animateMenuOpen ? 'animate' : '';\n const state = this.open ? 'open' : '';\n return `${animate} ${state}`;\n }\n\n get selectedMenuClass() {\n return this.selectedMenu ? 'open' : '';\n }\n\n get menuItems() {\n return this.menus.map(\n (menu: Record<string, any>) => html`\n <li>\n <menu-button\n @menuTypeSelected=${this.setSelectedMenu}\n .icon=${menu.icon}\n .label=${menu.label}\n .menuDetails=${menu.menuDetails}\n .id=${menu.id}\n .selected=${menu.id === this.selectedMenu}\n .followable=${menu.followable}\n .href=${menu.href}\n ></menu-button>\n </li>\n `,\n );\n }\n\n get renderMenuHeader() {\n const { label = '', menuDetails = '' } =\n this.selectedMenuDetails || ({} as Record<string, any>);\n const headerClass = this.selectedMenuAction ? 'with-secondary-action' : '';\n const actionBlock = this.selectedMenuAction\n ? html`<span class=\"custom-action\">${this.selectedMenuAction}</span>`\n : nothing;\n return html`\n <header class=\"${headerClass}\">\n <div class=\"details\">\n <h3>${label}</h3>\n <span class=\"extra-details\">${menuDetails}</span>\n </div>\n ${actionBlock} ${this.closeButton}\n </header>\n `;\n }\n\n get closeButton() {\n return html`\n <button\n class=\"close\"\n aria-label=\"Close this menu\"\n @click=${this.closeMenu}\n >\n <ia-icon-collapse-sidebar></ia-icon-collapse-sidebar>\n </button>\n `;\n }\n\n /** @inheritdoc */\n render() {\n return html`\n <div class=\"main\">\n <div class=\"menu ${this.sliderDetailsClass}\">\n ${this.closeButton}\n <ul class=\"menu-list\">\n ${this.menuItems}\n </ul>\n <div\n class=\"content ${this.selectedMenuClass}\"\n @menuTypeSelected=${this.setSelectedMenu}\n >\n ${this.renderMenuHeader}\n <section>\n <div class=\"selected-menu\">${this.selectedMenuComponent}</div>\n </section>\n </div>\n </div>\n </div>\n `;\n }\n}\n"]}
1
+ {"version":3,"file":"ia-menu-slider.js","sourceRoot":"","sources":["../../../src/menu-slider/ia-menu-slider.ts"],"names":[],"mappings":"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","sourcesContent":["import { LitElement, html, nothing } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport menuSliderCSS from './styles/menu-slider';\nimport '@internetarchive/icon-collapse-sidebar';\nimport './menu-button';\nimport { MenuProviderInterface } from '../interfaces/menu-interfaces';\n\nconst sliderEvents = {\n closeDrawer: 'menuSliderClosed',\n};\n\n@customElement('ia-menu-slider')\nexport class IaMenuSlider extends LitElement {\n static get styles() {\n return menuSliderCSS;\n }\n\n @property({ type: Array }) menus: MenuProviderInterface[] = [];\n\n @property({ type: Boolean }) open = false;\n\n @property({ type: Boolean }) manuallyHandleClose = false;\n\n @property({ type: String }) selectedMenu = '';\n\n @property({ type: Object }) selectedMenuAction = nothing;\n\n @property({ type: Boolean }) animateMenuOpen = false;\n\n updated() {\n const { actionButton } =\n this.selectedMenuDetails || ({} as Record<string, any>);\n const actionButtonHasChanged = actionButton !== this.selectedMenuAction;\n if (actionButtonHasChanged) {\n this.selectedMenuAction = actionButton || nothing;\n }\n }\n\n /**\n * Event handler, captures state of selected menu\n */\n setSelectedMenu({ detail }: CustomEvent) {\n const { id } = detail;\n this.selectedMenu = this.selectedMenu === id ? '' : id;\n const { actionButton } =\n this.selectedMenuDetails || ({} as Record<string, any>);\n this.selectedMenuAction = actionButton || nothing;\n }\n\n /**\n * closes menu drawer\n */\n closeMenu() {\n if (!this.manuallyHandleClose) {\n this.open = false;\n }\n const { closeDrawer } = sliderEvents;\n const drawerClosed = new CustomEvent(closeDrawer, {\n detail: this.selectedMenuDetails,\n });\n this.dispatchEvent(drawerClosed);\n }\n\n get selectedMenuDetails() {\n const selectedMenu = this.menus.find(\n menu => (menu as any).id === this.selectedMenu,\n );\n return selectedMenu;\n }\n\n get selectedMenuComponent() {\n const menuItem = this.selectedMenuDetails;\n return menuItem && (menuItem as any)?.component\n ? (menuItem as any).component\n : html``;\n }\n\n /* render */\n\n get sliderDetailsClass() {\n const animate = this.animateMenuOpen ? 'animate' : '';\n const state = this.open ? 'open' : '';\n return `${animate} ${state}`;\n }\n\n get selectedMenuClass() {\n return this.selectedMenu ? 'open' : '';\n }\n\n get menuItems() {\n return this.menus.map(\n (menu: Record<string, any>) => html`\n <li>\n <menu-button\n @menuTypeSelected=${this.setSelectedMenu}\n .icon=${menu.icon}\n .label=${menu.label}\n .menuDetails=${menu.menuDetails}\n .buttonId=${menu.id}\n .selected=${menu.id === this.selectedMenu}\n .followable=${menu.followable}\n .href=${menu.href}\n ></menu-button>\n </li>\n `,\n );\n }\n\n get renderMenuHeader() {\n const { label = '', menuDetails = '' } =\n this.selectedMenuDetails || ({} as Record<string, any>);\n const headerClass = this.selectedMenuAction ? 'with-secondary-action' : '';\n const actionBlock = this.selectedMenuAction\n ? html`<span class=\"custom-action\">${this.selectedMenuAction}</span>`\n : nothing;\n return html`\n <header class=\"${headerClass}\">\n <div class=\"details\">\n <h3>${label}</h3>\n <span class=\"extra-details\">${menuDetails}</span>\n </div>\n ${actionBlock} ${this.closeButton}\n </header>\n `;\n }\n\n get closeButton() {\n return html`\n <button\n class=\"close\"\n aria-label=\"Close this menu\"\n @click=${this.closeMenu}\n >\n <ia-icon-collapse-sidebar></ia-icon-collapse-sidebar>\n </button>\n `;\n }\n\n /** @inheritdoc */\n render() {\n return html`\n <div class=\"main\">\n <div class=\"menu ${this.sliderDetailsClass}\">\n ${this.closeButton}\n <ul class=\"menu-list\">\n ${this.menuItems}\n </ul>\n <div\n class=\"content ${this.selectedMenuClass}\"\n @menuTypeSelected=${this.setSelectedMenu}\n >\n ${this.renderMenuHeader}\n <section>\n <div class=\"selected-menu\">${this.selectedMenuComponent}</div>\n </section>\n </div>\n </div>\n </div>\n `;\n }\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@internetarchive/ia-item-navigator",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "Internet Archive's Item Navigator, visually explore an item's contents.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -96,7 +96,7 @@ export class IaMenuSlider extends LitElement {
96
96
  .icon=${menu.icon}
97
97
  .label=${menu.label}
98
98
  .menuDetails=${menu.menuDetails}
99
- .id=${menu.id}
99
+ .buttonId=${menu.id}
100
100
  .selected=${menu.id === this.selectedMenu}
101
101
  .followable=${menu.followable}
102
102
  .href=${menu.href}
@@ -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
+ });
@@ -0,0 +1,84 @@
1
+ import { html, fixture, expect } from '@open-wc/testing';
2
+ import sinon from 'sinon';
3
+ import '../src/menus/iaux-sharing-options';
4
+ import type { IauxSharingOptions } from '../src/menus/iaux-sharing-options';
5
+
6
+ const identifier = 'goody';
7
+ const itemType = 'book';
8
+ const creator = 'Welsh, Charles';
9
+ const description =
10
+ 'The history of Little Goody Two-Shoes : otherwise called Mrs. Margery Two-Shoes ... [1766 edition]';
11
+
12
+ const container = (optionalFileSubprefix = '') =>
13
+ html`<iaux-sharing-options
14
+ identifier="${identifier}"
15
+ type="${itemType}"
16
+ creator="${creator}"
17
+ description="${description}"
18
+ baseHost="archive.org"
19
+ fileSubPrefix="${optionalFileSubprefix}"
20
+ ></iaux-sharing-options>`;
21
+
22
+ describe('<iaux-sharing-options>', () => {
23
+ afterEach(() => {
24
+ sinon.restore();
25
+ });
26
+
27
+ it('sets default properties', async () => {
28
+ const el = (await fixture(container())) as IauxSharingOptions;
29
+
30
+ expect(el.identifier).to.equal(identifier);
31
+ expect(el.type).to.equal(itemType);
32
+ expect(el.creator).to.equal(creator);
33
+ expect(el.description).to.equal(description);
34
+ });
35
+
36
+ it('renders buttons for each sharing method', async () => {
37
+ const el = (await fixture(container())) as IauxSharingOptions;
38
+
39
+ await el.updateComplete;
40
+
41
+ el.sharingOptions.forEach(option => {
42
+ const button =
43
+ el.shadowRoot && el.shadowRoot.querySelector(`a.${option.class}`);
44
+ expect(button).to.exist;
45
+ expect(button?.getAttribute('href')).to.equal(option.url);
46
+ });
47
+ });
48
+
49
+ it('toggles visibility of embed options', async () => {
50
+ const el = (await fixture(container())) as IauxSharingOptions;
51
+
52
+ el.toggleEmbedOptions(new Event('click'));
53
+ await el.updateComplete;
54
+
55
+ expect(el.embedOptionsVisible).to.equal(true);
56
+ });
57
+
58
+ it('does not show internal header by default', async () => {
59
+ const el = (await fixture(container())) as IauxSharingOptions;
60
+ expect(el.shadowRoot?.querySelector('header')).to.be.null;
61
+ });
62
+
63
+ it('does shows internal header when requested', async () => {
64
+ const el = (await fixture(container())) as IauxSharingOptions;
65
+ el.renderHeader = true;
66
+ await el.updateComplete;
67
+ expect(el.shadowRoot?.querySelector('header')).to.not.be.null;
68
+ });
69
+
70
+ it('sets file subprefix to end of share URLs if present', async () => {
71
+ const optionalFileSubprefix = 'foo- bar - 123-';
72
+ const el = (await fixture(
73
+ container(optionalFileSubprefix),
74
+ )) as IauxSharingOptions;
75
+
76
+ el.sharingOptions.forEach(option => {
77
+ if (option.name !== 'Tumblr') {
78
+ expect(option.url).to.contain(
79
+ encodeURIComponent(optionalFileSubprefix),
80
+ );
81
+ }
82
+ });
83
+ });
84
+ });