@internetarchive/ia-item-navigator 1.1.0 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+ });