@internetarchive/ia-item-navigator 2.1.3-alpha.1 → 2.2.0

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 (50) hide show
  1. package/.github/workflows/ci.yml +1 -4
  2. package/.github/workflows/gh-pages-main.yml +5 -5
  3. package/.github/workflows/pr-preview.yml +29 -4
  4. package/demo/app-root.ts +5 -5
  5. package/demo/index.html +0 -1
  6. package/dist/demo/app-root.d.ts +2 -2
  7. package/dist/demo/app-root.js +1 -2
  8. package/dist/demo/app-root.js.map +1 -1
  9. package/dist/src/iaux-item-navigator.d.ts +4 -2
  10. package/dist/src/iaux-item-navigator.js +43 -13
  11. package/dist/src/iaux-item-navigator.js.map +1 -1
  12. package/dist/src/interfaces/menu-interfaces.d.ts +4 -5
  13. package/dist/src/interfaces/menu-interfaces.js.map +1 -1
  14. package/dist/src/loader.js +0 -1
  15. package/dist/src/loader.js.map +1 -1
  16. package/dist/src/menu-slider/ia-menu-slider.d.ts +13 -7
  17. package/dist/src/menu-slider/ia-menu-slider.js +65 -20
  18. package/dist/src/menu-slider/ia-menu-slider.js.map +1 -1
  19. package/dist/src/menu-slider/menu-button.d.ts +15 -7
  20. package/dist/src/menu-slider/menu-button.js +12 -3
  21. package/dist/src/menu-slider/menu-button.js.map +1 -1
  22. package/dist/src/menu-slider/styles/menu-button.js +14 -4
  23. package/dist/src/menu-slider/styles/menu-button.js.map +1 -1
  24. package/dist/src/menu-slider/styles/menu-slider.js +7 -9
  25. package/dist/src/menu-slider/styles/menu-slider.js.map +1 -1
  26. package/dist/src/menus/share-panel.js +15 -11
  27. package/dist/src/menus/share-panel.js.map +1 -1
  28. package/dist/src/menus/viewable-files.js +0 -2
  29. package/dist/src/menus/viewable-files.js.map +1 -1
  30. package/dist/test/ia-stub.js +2 -1
  31. package/dist/test/ia-stub.js.map +1 -1
  32. package/dist/test/iaux-item-navigator.test.js +1 -1
  33. package/dist/test/iaux-item-navigator.test.js.map +1 -1
  34. package/dist/test/no-theater-available.test.js +0 -1
  35. package/dist/test/no-theater-available.test.js.map +1 -1
  36. package/eslint.config.mjs +53 -0
  37. package/package.json +30 -60
  38. package/src/iaux-item-navigator.ts +42 -14
  39. package/src/interfaces/menu-interfaces.ts +4 -8
  40. package/src/loader.ts +0 -1
  41. package/src/menu-slider/ia-menu-slider.ts +67 -28
  42. package/src/menu-slider/menu-button.ts +14 -4
  43. package/src/menu-slider/styles/menu-button.ts +14 -4
  44. package/src/menu-slider/styles/menu-slider.ts +7 -9
  45. package/src/menus/share-panel.ts +15 -11
  46. package/src/menus/viewable-files.ts +0 -2
  47. package/test/ia-stub.ts +1 -2
  48. package/test/iaux-item-navigator.test.ts +1 -1
  49. package/test/no-theater-available.test.ts +0 -1
  50. package/web-test-runner.config.mjs +3 -0
@@ -5,25 +5,21 @@ export type MenuId = string;
5
5
  export interface MenuShortcutInterface {
6
6
  icon: TemplateResult;
7
7
  id: MenuId;
8
+ label: string;
8
9
  }
9
10
 
10
11
  export interface MenuDetailsInterface extends MenuShortcutInterface {
11
- label: string;
12
12
  menuDetails?: TemplateResult;
13
13
  selected?: boolean;
14
14
  followable?: boolean;
15
15
  href?: string;
16
+ component?: TemplateResult;
16
17
  }
17
18
 
18
- export interface MenuProviderBaseConfigInterface {
19
+ export interface MenuProviderInterface extends MenuDetailsInterface {
19
20
  item: MetadataResponse;
20
21
  baseHost: string;
21
22
  subPrefix: string;
22
23
  updated?: any;
23
- }
24
- export interface MenuProviderInterface
25
- extends MenuProviderBaseConfigInterface,
26
- MenuDetailsInterface,
27
- MenuShortcutInterface {
28
- actionButton: TemplateResult;
24
+ actionButton?: TemplateResult;
29
25
  }
package/src/loader.ts CHANGED
@@ -1,4 +1,3 @@
1
- /* eslint-disable class-methods-use-this */
2
1
  import { css, html, LitElement, svg, nothing } from 'lit';
3
2
  import { customElement, property } from 'lit/decorators.js';
4
3
 
@@ -1,5 +1,5 @@
1
- import { LitElement, html, nothing } from 'lit';
2
- import { customElement, property } from 'lit/decorators.js';
1
+ import { LitElement, TemplateResult, html, nothing } from 'lit';
2
+ import { customElement, property, query } from 'lit/decorators.js';
3
3
  import menuSliderCSS from './styles/menu-slider';
4
4
  import '@internetarchive/icon-collapse-sidebar';
5
5
  import './menu-button';
@@ -23,16 +23,21 @@ export class IaMenuSlider extends LitElement {
23
23
 
24
24
  @property({ type: String }) selectedMenu = '';
25
25
 
26
- @property({ type: Object }) selectedMenuAction = nothing;
26
+ @property({ type: Object }) selectedMenuAction:
27
+ | TemplateResult
28
+ | typeof nothing = nothing;
27
29
 
28
30
  @property({ type: Boolean }) animateMenuOpen = false;
29
31
 
32
+ @query('.content.open button.close') contentCloseButton!: HTMLElement;
33
+
34
+ @query('.menu-list') menuList!: HTMLUListElement;
35
+
30
36
  updated() {
31
- const { actionButton } =
32
- this.selectedMenuDetails || ({} as Record<string, any>);
37
+ const actionButton = this.selectedMenuDetails?.actionButton || nothing;
33
38
  const actionButtonHasChanged = actionButton !== this.selectedMenuAction;
34
39
  if (actionButtonHasChanged) {
35
- this.selectedMenuAction = actionButton || nothing;
40
+ this.selectedMenuAction = actionButton;
36
41
  }
37
42
  }
38
43
 
@@ -42,9 +47,10 @@ export class IaMenuSlider extends LitElement {
42
47
  setSelectedMenu({ detail }: CustomEvent) {
43
48
  const { id } = detail;
44
49
  this.selectedMenu = this.selectedMenu === id ? '' : id;
45
- const { actionButton } =
46
- this.selectedMenuDetails || ({} as Record<string, any>);
47
- this.selectedMenuAction = actionButton || nothing;
50
+ this.selectedMenuAction = this.selectedMenuDetails?.actionButton || nothing;
51
+ this.updateComplete.then(() => {
52
+ this.contentCloseButton?.focus();
53
+ });
48
54
  }
49
55
 
50
56
  /**
@@ -61,18 +67,41 @@ export class IaMenuSlider extends LitElement {
61
67
  this.dispatchEvent(drawerClosed);
62
68
  }
63
69
 
64
- get selectedMenuDetails() {
65
- const selectedMenu = this.menus.find(
66
- menu => (menu as any).id === this.selectedMenu,
67
- );
68
- return selectedMenu;
70
+ closePanel() {
71
+ const menuId = this.selectedMenu;
72
+ this.selectedMenu = '';
73
+ this.selectedMenuAction = nothing;
74
+
75
+ // Return focus to the menu button that was previously selected
76
+ if (menuId) {
77
+ this.updateComplete.then(() => {
78
+ const menuIndex = this.menus.findIndex(menu => menu.id === menuId);
79
+ if (menuIndex !== -1) {
80
+ const menuButton = this.menuList.querySelector(
81
+ `li:nth-child(${menuIndex + 1}) menu-button`,
82
+ ) as HTMLElement;
83
+ menuButton?.focus();
84
+ }
85
+ });
86
+ }
69
87
  }
70
88
 
71
- get selectedMenuComponent() {
72
- const menuItem = this.selectedMenuDetails;
73
- return menuItem && (menuItem as any)?.component
74
- ? (menuItem as any).component
75
- : html``;
89
+ /**
90
+ * Handle keyboard events, specifically ESC key to close menu details
91
+ */
92
+ handleKeyDown(event: KeyboardEvent) {
93
+ if (event.key === 'Escape') {
94
+ event.preventDefault();
95
+ if (this.selectedMenu) {
96
+ this.closePanel();
97
+ } else {
98
+ this.closeMenu();
99
+ }
100
+ }
101
+ }
102
+
103
+ get selectedMenuDetails() {
104
+ return this.menus.find(menu => menu.id === this.selectedMenu);
76
105
  }
77
106
 
78
107
  /* render */
@@ -89,17 +118,17 @@ export class IaMenuSlider extends LitElement {
89
118
 
90
119
  get menuItems() {
91
120
  return this.menus.map(
92
- (menu: Record<string, any>) => html`
121
+ menu => html`
93
122
  <li>
94
123
  <menu-button
95
124
  @menuTypeSelected=${this.setSelectedMenu}
96
125
  .icon=${menu.icon}
97
126
  .label=${menu.label}
98
- .menuDetails=${menu.menuDetails}
127
+ .menuDetails=${menu.menuDetails || ''}
99
128
  .buttonId=${menu.id}
100
129
  .selected=${menu.id === this.selectedMenu}
101
- .followable=${menu.followable}
102
- .href=${menu.href}
130
+ .followable=${menu.followable || false}
131
+ .href=${menu.href || ''}
103
132
  ></menu-button>
104
133
  </li>
105
134
  `,
@@ -107,8 +136,7 @@ export class IaMenuSlider extends LitElement {
107
136
  }
108
137
 
109
138
  get renderMenuHeader() {
110
- const { label = '', menuDetails = '' } =
111
- this.selectedMenuDetails || ({} as Record<string, any>);
139
+ const { label = '', menuDetails = '' } = this.selectedMenuDetails || {};
112
140
  const headerClass = this.selectedMenuAction ? 'with-secondary-action' : '';
113
141
  const actionBlock = this.selectedMenuAction
114
142
  ? html`<span class="custom-action">${this.selectedMenuAction}</span>`
@@ -119,7 +147,15 @@ export class IaMenuSlider extends LitElement {
119
147
  <h3>${label}</h3>
120
148
  <span class="extra-details">${menuDetails}</span>
121
149
  </div>
122
- ${actionBlock} ${this.closeButton}
150
+ ${actionBlock}
151
+ <button
152
+ class="close"
153
+ aria-label="Close this menu"
154
+ title="Close this menu"
155
+ @click=${this.closePanel}
156
+ >
157
+ <ia-icon-collapse-sidebar></ia-icon-collapse-sidebar>
158
+ </button>
123
159
  </header>
124
160
  `;
125
161
  }
@@ -129,6 +165,7 @@ export class IaMenuSlider extends LitElement {
129
165
  <button
130
166
  class="close"
131
167
  aria-label="Close this menu"
168
+ title="Close this menu"
132
169
  @click=${this.closeMenu}
133
170
  >
134
171
  <ia-icon-collapse-sidebar></ia-icon-collapse-sidebar>
@@ -139,7 +176,7 @@ export class IaMenuSlider extends LitElement {
139
176
  /** @inheritdoc */
140
177
  render() {
141
178
  return html`
142
- <div class="main">
179
+ <div class="main" @keydown=${this.handleKeyDown}>
143
180
  <div class="menu ${this.sliderDetailsClass}">
144
181
  ${this.closeButton}
145
182
  <ul class="menu-list">
@@ -151,7 +188,9 @@ export class IaMenuSlider extends LitElement {
151
188
  >
152
189
  ${this.renderMenuHeader}
153
190
  <section>
154
- <div class="selected-menu">${this.selectedMenuComponent}</div>
191
+ <div class="selected-menu">
192
+ ${this.selectedMenuDetails?.component || nothing}
193
+ </div>
155
194
  </section>
156
195
  </div>
157
196
  </div>
@@ -1,4 +1,4 @@
1
- import { html, LitElement } from 'lit';
1
+ import { html, LitElement, TemplateResult } from 'lit';
2
2
  import { customElement, property } from 'lit/decorators.js';
3
3
  import menuButtonCSS from './styles/menu-button';
4
4
 
@@ -8,13 +8,18 @@ export class MenuButton extends LitElement {
8
8
  return menuButtonCSS;
9
9
  }
10
10
 
11
- @property({ type: String }) icon = '';
11
+ static shadowRootOptions = {
12
+ ...LitElement.shadowRootOptions,
13
+ delegatesFocus: true,
14
+ };
15
+
16
+ @property({ type: Object }) icon: TemplateResult | string = '';
12
17
 
13
18
  @property({ type: String }) href = '';
14
19
 
15
20
  @property({ type: String }) label = '';
16
21
 
17
- @property({ type: String }) menuDetails = '';
22
+ @property({ type: Object }) menuDetails: TemplateResult | string = '';
18
23
 
19
24
  @property({ type: String }) buttonId = '';
20
25
 
@@ -49,7 +54,12 @@ export class MenuButton extends LitElement {
49
54
 
50
55
  get menuItem() {
51
56
  return html`
52
- <span class="icon ${this.iconClass}"> ${this.icon} </span>
57
+ <span
58
+ class="icon ${this.iconClass}"
59
+ aria-hidden="true"
60
+ title="${this.label}"
61
+ >${this.icon}</span
62
+ >
53
63
  <span class="label">${this.label}</span>
54
64
  <span class="menu-details">${this.menuDetails}</span>
55
65
  `;
@@ -6,6 +6,11 @@ export default css`
6
6
  text-decoration: none;
7
7
  }
8
8
 
9
+ button.menu-item {
10
+ -webkit-appearance: none;
11
+ appearance: none;
12
+ }
13
+
9
14
  .menu-item {
10
15
  display: inline-flex;
11
16
  width: 100%;
@@ -18,12 +23,12 @@ export default css`
18
23
  align-items: center;
19
24
  border: none;
20
25
  cursor: pointer;
26
+ transition: background-color 0.2s;
27
+ border-radius: 6px;
21
28
  }
22
29
 
23
- button.menu-item {
24
- -webkit-appearance: none;
25
- appearance: none;
26
- border-radius: 0;
30
+ .menu-item:hover {
31
+ background-color: rgba(255, 255, 255, 0.1);
27
32
  }
28
33
 
29
34
  .label {
@@ -60,6 +65,11 @@ export default css`
60
65
  justify-content: center;
61
66
  }
62
67
 
68
+ .menu-item > .icon > * {
69
+ /* Prevent tooltip containing icon literal description */
70
+ pointer-events: none;
71
+ }
72
+
63
73
  .menu-item.selected .icon {
64
74
  background-color: var(--activeButtonBg);
65
75
  border-radius: 1rem 0 0 1rem;
@@ -29,10 +29,6 @@ export default css`
29
29
  transform: translateX(calc(${sliderWidth} * -1));
30
30
  }
31
31
 
32
- .menu > button.close {
33
- right: 0.7rem;
34
- }
35
-
36
32
  button {
37
33
  cursor: pointer;
38
34
  }
@@ -72,11 +68,13 @@ export default css`
72
68
  position: absolute;
73
69
  }
74
70
  button.close {
75
- right: 0.5rem;
76
- }
77
-
78
- button.close * {
79
- float: right;
71
+ min-width: 38px;
72
+ min-height: 38px;
73
+ display: flex;
74
+ justify-content: center;
75
+ align-items: center;
76
+ right: 0;
77
+ top: 0;
80
78
  }
81
79
 
82
80
  .content {
@@ -1,5 +1,3 @@
1
- /* eslint-disable lit-a11y/click-events-have-key-events */
2
- /* eslint-disable lit-a11y/list */
3
1
  import {
4
2
  css,
5
3
  CSSResult,
@@ -83,7 +81,7 @@ export class IauxSharingOptions extends LitElement {
83
81
  this.sharingOptions = [
84
82
  {
85
83
  name: 'Twitter',
86
- icon: html`<ia-icon-twitter></ia-icon-twitter>`,
84
+ icon: html`<ia-icon-twitter aria-hidden="true"></ia-icon-twitter>`,
87
85
  url: `https://twitter.com/intent/tweet?${new URLSearchParams({
88
86
  url: shareUrl,
89
87
  text: shareBlurb,
@@ -92,14 +90,14 @@ export class IauxSharingOptions extends LitElement {
92
90
  },
93
91
  {
94
92
  name: 'Facebook',
95
- icon: html`<ia-icon-facebook></ia-icon-facebook>`,
93
+ icon: html`<ia-icon-facebook aria-hidden="true"></ia-icon-facebook>`,
96
94
  url: `https://www.facebook.com/sharer/sharer.php?${new URLSearchParams({
97
95
  u: shareUrl,
98
96
  })}`,
99
97
  },
100
98
  {
101
99
  name: 'Tumblr',
102
- icon: html`<ia-icon-tumblr></ia-icon-tumblr>`,
100
+ icon: html`<ia-icon-tumblr aria-hidden="true"></ia-icon-tumblr>`,
103
101
  url: `https://www.tumblr.com/widgets/share/tool/preview?${new URLSearchParams(
104
102
  {
105
103
  posttype: 'link',
@@ -110,7 +108,7 @@ export class IauxSharingOptions extends LitElement {
110
108
  },
111
109
  {
112
110
  name: 'Pinterest',
113
- icon: html`<ia-icon-pinterest></ia-icon-pinterest>`,
111
+ icon: html`<ia-icon-pinterest aria-hidden="true"></ia-icon-pinterest>`,
114
112
  url: `http://www.pinterest.com/pin/create/button/?${new URLSearchParams(
115
113
  {
116
114
  url: shareUrl,
@@ -120,7 +118,7 @@ export class IauxSharingOptions extends LitElement {
120
118
  },
121
119
  {
122
120
  name: 'Email',
123
- icon: html`<ia-icon-email></ia-icon-email>`,
121
+ icon: html`<ia-icon-email aria-hidden="true"></ia-icon-email>`,
124
122
  url: `mailto:?${new URLSearchParams({
125
123
  subject: shareBlurb,
126
124
  body: shareUrl,
@@ -153,7 +151,7 @@ export class IauxSharingOptions extends LitElement {
153
151
  render() {
154
152
  return html`
155
153
  ${this.header}
156
- <main>
154
+ <div>
157
155
  ${this.sharingOptions.map(
158
156
  option =>
159
157
  html` <a class="share-option" href="${option.url}" target="_blank">
@@ -162,7 +160,7 @@ export class IauxSharingOptions extends LitElement {
162
160
  )}
163
161
  <details>
164
162
  <summary class="share-option">
165
- <ia-icon-link></ia-icon-link>
163
+ <ia-icon-link aria-hidden="true"></ia-icon-link>
166
164
  Get an embeddable link
167
165
  </summary>
168
166
  <div class="embed">
@@ -187,7 +185,7 @@ export class IauxSharingOptions extends LitElement {
187
185
  </p>
188
186
  </div>
189
187
  </details>
190
- </main>
188
+ </div>
191
189
  `;
192
190
  }
193
191
 
@@ -222,7 +220,7 @@ export class IauxSharingOptions extends LitElement {
222
220
  font-size: 1.4rem;
223
221
  }
224
222
 
225
- main {
223
+ :host > div {
226
224
  padding: 1rem 0;
227
225
  }
228
226
 
@@ -233,6 +231,12 @@ export class IauxSharingOptions extends LitElement {
233
231
  text-decoration: none;
234
232
  color: var(--shareLinkColor);
235
233
  cursor: pointer;
234
+ transition: background-color 0.2s;
235
+ border-radius: 6px;
236
+ }
237
+
238
+ .share-option:hover {
239
+ background-color: rgba(255, 255, 255, 0.05);
236
240
  }
237
241
 
238
242
  .share-option > * {
@@ -1,5 +1,3 @@
1
- /* eslint-disable max-classes-per-file */
2
- /* eslint-disable lit-a11y/list */
3
1
  import { css, html, LitElement, nothing, TemplateResult } from 'lit';
4
2
  import { customElement, property } from 'lit/decorators.js';
5
3
  import { repeat } from 'lit/directives/repeat.js';
package/test/ia-stub.ts CHANGED
@@ -1,4 +1,3 @@
1
- /* eslint-disable camelcase */
2
1
  import { Metadata } from '@internetarchive/iaux-item-metadata';
3
2
  import {
4
3
  File,
@@ -23,7 +22,7 @@ export class ItemStub implements MetadataResponse {
23
22
  this.files_count = 0;
24
23
  this.item_last_updated = 2020;
25
24
  this.item_size = 111;
26
- this.metadata = { identifier: 'foo' } as Metadata;
25
+ this.metadata = new Metadata({ identifier: 'foo' });
27
26
  this.server = 'foo-server';
28
27
  this.uniq = 2;
29
28
  this.workable_servers = ['abc'];
@@ -1,4 +1,3 @@
1
- /* eslint-disable camelcase */
2
1
  import { html, fixture, expect } from '@open-wc/testing';
3
2
  import Sinon from 'sinon';
4
3
 
@@ -344,6 +343,7 @@ describe('ItemNavigator', () => {
344
343
  const anotherShortcut = {
345
344
  id: 'foo',
346
345
  icon: html`<i class="foo-shortcut"></i>`,
346
+ label: 'Foo',
347
347
  };
348
348
  el.menuContents = [menuProvider];
349
349
  el.menuShortcuts = [shortcut, anotherShortcut];
@@ -11,7 +11,6 @@ describe('IANoTheaterAvailable', () => {
11
11
  );
12
12
  let eventFired = false;
13
13
  el.addEventListener('loadingStateUpdated', () => {
14
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
15
14
  eventFired = true;
16
15
  });
17
16
  expect(eventFired).to.be.false;
@@ -3,6 +3,9 @@
3
3
  export default /** @type {import("@web/test-runner").TestRunnerConfig} */ ({
4
4
  files: 'dist/test/**/*.test.js',
5
5
  nodeResolve: true,
6
+ coverageConfig: {
7
+ exclude: ['./dist/test/**/*', './node_modules/**/*'],
8
+ },
6
9
 
7
10
  /** Compile JS for older browsers. Requires @web/dev-server-esbuild plugin */
8
11
  // esbuildTarget: 'auto',