@internetarchive/collection-browser 0.4.10 → 0.4.12

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 (40) hide show
  1. package/dist/src/collection-browser.d.ts +1 -0
  2. package/dist/src/collection-browser.js +24 -5
  3. package/dist/src/collection-browser.js.map +1 -1
  4. package/dist/src/collection-facets/more-facets-content.d.ts +3 -2
  5. package/dist/src/collection-facets/more-facets-content.js +24 -47
  6. package/dist/src/collection-facets/more-facets-content.js.map +1 -1
  7. package/dist/src/collection-facets/more-facets-pagination.js +5 -3
  8. package/dist/src/collection-facets/more-facets-pagination.js.map +1 -1
  9. package/dist/src/collection-facets/toggle-switch.d.ts +41 -0
  10. package/dist/src/collection-facets/toggle-switch.js +184 -0
  11. package/dist/src/collection-facets/toggle-switch.js.map +1 -0
  12. package/dist/src/empty-placeholder.d.ts +5 -3
  13. package/dist/src/empty-placeholder.js +39 -3
  14. package/dist/src/empty-placeholder.js.map +1 -1
  15. package/dist/src/sort-filter-bar/alpha-bar.js +8 -0
  16. package/dist/src/sort-filter-bar/alpha-bar.js.map +1 -1
  17. package/dist/src/tiles/list/tile-list-compact.d.ts +1 -0
  18. package/dist/src/tiles/list/tile-list-compact.js +15 -2
  19. package/dist/src/tiles/list/tile-list-compact.js.map +1 -1
  20. package/dist/src/tiles/list/tile-list.js +3 -0
  21. package/dist/src/tiles/list/tile-list.js.map +1 -1
  22. package/dist/test/collection-facets/toggle-switch.test.d.ts +1 -0
  23. package/dist/test/collection-facets/toggle-switch.test.js +87 -0
  24. package/dist/test/collection-facets/toggle-switch.test.js.map +1 -0
  25. package/dist/test/tiles/list/tile-list-compact.test.js +12 -0
  26. package/dist/test/tiles/list/tile-list-compact.test.js.map +1 -1
  27. package/dist/test/tiles/list/tile-list.test.js +12 -0
  28. package/dist/test/tiles/list/tile-list.test.js.map +1 -1
  29. package/package.json +3 -3
  30. package/src/collection-browser.ts +25 -1
  31. package/src/collection-facets/more-facets-content.ts +25 -48
  32. package/src/collection-facets/more-facets-pagination.ts +5 -3
  33. package/src/collection-facets/toggle-switch.ts +184 -0
  34. package/src/empty-placeholder.ts +53 -7
  35. package/src/sort-filter-bar/alpha-bar.ts +8 -0
  36. package/src/tiles/list/tile-list-compact.ts +15 -2
  37. package/src/tiles/list/tile-list.ts +3 -0
  38. package/test/collection-facets/toggle-switch.test.ts +154 -0
  39. package/test/tiles/list/tile-list-compact.test.ts +14 -0
  40. package/test/tiles/list/tile-list.test.ts +14 -0
@@ -1,22 +1,35 @@
1
- import { css, html, LitElement, CSSResultGroup, nothing } from 'lit';
1
+ import {
2
+ css,
3
+ html,
4
+ LitElement,
5
+ CSSResultGroup,
6
+ nothing,
7
+ TemplateResult,
8
+ } from 'lit';
2
9
  import { customElement, property } from 'lit/decorators.js';
3
10
  import { choose } from 'lit/directives/choose.js';
4
11
 
5
12
  import emptyQueryIcon from './assets/img/icons/empty-query';
6
13
  import nullResultIcon from './assets/img/icons/null-result';
7
14
 
8
- export type PlaceholderType = 'empty-query' | 'null-result' | null;
15
+ export type PlaceholderType =
16
+ | 'empty-query'
17
+ | 'null-result'
18
+ | 'query-error'
19
+ | null;
9
20
  @customElement('empty-placeholder')
10
21
  export class EmptyPlaceholder extends LitElement {
11
22
  @property({ type: String }) placeholderType: PlaceholderType = null;
12
23
 
13
24
  @property({ type: Boolean }) isMobileView?: false;
14
25
 
26
+ @property({ type: String }) detailMessage?: string = '';
27
+
15
28
  render() {
16
29
  return this.placeholderType ? html`${this.placeholderTemplate}` : nothing;
17
30
  }
18
31
 
19
- private get placeholderTemplate() {
32
+ private get placeholderTemplate(): TemplateResult {
20
33
  return html`
21
34
  <div
22
35
  class="placeholder ${this.placeholderType} ${this.isMobileView
@@ -26,12 +39,13 @@ export class EmptyPlaceholder extends LitElement {
26
39
  ${choose(this.placeholderType, [
27
40
  ['empty-query', () => this.emptyQueryTemplate],
28
41
  ['null-result', () => this.nullResultTemplate],
42
+ ['query-error', () => this.queryErrorTemplate],
29
43
  ])}
30
44
  </div>
31
45
  `;
32
46
  }
33
47
 
34
- private get emptyQueryTemplate() {
48
+ private get emptyQueryTemplate(): TemplateResult {
35
49
  return html`
36
50
  <h2 class="title">
37
51
  To begin searching, enter a search term in the box above and hit "Go".
@@ -40,7 +54,7 @@ export class EmptyPlaceholder extends LitElement {
40
54
  `;
41
55
  }
42
56
 
43
- private get nullResultTemplate() {
57
+ private get nullResultTemplate(): TemplateResult {
44
58
  return html`
45
59
  <h2 class="title">
46
60
  Your search did not match any items in the Archive. Try different
@@ -50,6 +64,22 @@ export class EmptyPlaceholder extends LitElement {
50
64
  `;
51
65
  }
52
66
 
67
+ private get queryErrorTemplate(): TemplateResult {
68
+ return html`
69
+ <h2 class="title">
70
+ The search engine encountered an error, which might be related to your
71
+ search query.
72
+ <a
73
+ href="https://help.archive.org/help/search-building-powerful-complex-queries/"
74
+ >
75
+ Tips for constructing search queries.
76
+ </a>
77
+ </h2>
78
+ <div>${nullResultIcon}</div>
79
+ <p class="error-details">Error details: ${this.detailMessage}</p>
80
+ `;
81
+ }
82
+
53
83
  static get styles(): CSSResultGroup {
54
84
  return css`
55
85
  :host {
@@ -57,6 +87,16 @@ export class EmptyPlaceholder extends LitElement {
57
87
  width: 100%;
58
88
  }
59
89
 
90
+ a {
91
+ text-decoration: none;
92
+ }
93
+ a:link {
94
+ color: var(--ia-theme-link-color, #4b64ff);
95
+ }
96
+ a:hover {
97
+ text-decoration: underline;
98
+ }
99
+
60
100
  .placeholder {
61
101
  display: block;
62
102
  }
@@ -64,16 +104,22 @@ export class EmptyPlaceholder extends LitElement {
64
104
  .desktop svg {
65
105
  max-height: 40rem;
66
106
  }
67
- .desktop .title {
107
+ .desktop .title,
108
+ .desktop .error-details {
68
109
  margin: 4rem 0;
69
110
  }
70
111
 
71
112
  .mobile svg {
72
113
  max-height: 20rem;
73
114
  }
74
- .mobile .title {
115
+ .mobile .title,
116
+ .mobile .error-details {
75
117
  margin: 2rem 0.5;
76
118
  }
119
+
120
+ .error-details {
121
+ font-size: 1.2rem;
122
+ }
77
123
  `;
78
124
  }
79
125
  }
@@ -233,5 +233,13 @@ export class AlphaBar extends LitElement {
233
233
  alpha-bar-tooltip.fade-in {
234
234
  opacity: 1;
235
235
  }
236
+
237
+ /* Make alphabet bar a 2-row container in small screen widths */
238
+ @media screen and (max-width: 768px) {
239
+ ul {
240
+ display: grid;
241
+ grid-template-columns: repeat(13, 1fr);
242
+ }
243
+ }
236
244
  `;
237
245
  }
@@ -41,7 +41,9 @@ export class TileListCompact extends LitElement {
41
41
  .loggedIn=${this.loggedIn}
42
42
  >
43
43
  </image-block>
44
- <div id="title">${DOMPurify.sanitize(this.model?.title ?? '')}</div>
44
+ <a href=${this.href} id="title"
45
+ >${DOMPurify.sanitize(this.model?.title ?? '')}</a
46
+ >
45
47
  <div id="creator">
46
48
  ${this.model?.mediatype === 'account'
47
49
  ? accountLabel(this.model?.dateAdded)
@@ -60,6 +62,14 @@ export class TileListCompact extends LitElement {
60
62
  `;
61
63
  }
62
64
 
65
+ private get href(): string {
66
+ // Use the server-specified href if available.
67
+ // Otherwise, construct a details page URL from the item identifier.
68
+ return this.model?.href
69
+ ? `${this.baseNavigationUrl}${this.model.href}`
70
+ : `${this.baseNavigationUrl}/details/${this.model?.identifier}`;
71
+ }
72
+
63
73
  /*
64
74
  * TODO: fix field names to match model in src/collection-browser.ts
65
75
  * private get dateSortSelector()
@@ -145,10 +155,13 @@ export class TileListCompact extends LitElement {
145
155
  }
146
156
 
147
157
  #title {
148
- color: #4b64ff;
149
158
  text-decoration: none;
150
159
  }
151
160
 
161
+ #title:link {
162
+ color: var(--ia-theme-link-color, #4b64ff);
163
+ }
164
+
152
165
  #title,
153
166
  #creator {
154
167
  text-overflow: ellipsis;
@@ -453,6 +453,9 @@ export class TileList extends LitElement {
453
453
 
454
454
  div a {
455
455
  text-decoration: none;
456
+ }
457
+
458
+ div a:link {
456
459
  color: var(--ia-theme-link-color, #4b64ff);
457
460
  }
458
461
 
@@ -0,0 +1,154 @@
1
+ import { expect, fixture } from '@open-wc/testing';
2
+ import sinon from 'sinon';
3
+ import { html } from 'lit';
4
+ import type { ToggleSwitch } from '../../src/collection-facets/toggle-switch';
5
+
6
+ import '../../src/collection-facets/toggle-switch';
7
+
8
+ describe('Toggle switch', () => {
9
+ it('renders component', async () => {
10
+ const el = await fixture<ToggleSwitch>(
11
+ html`<toggle-switch></toggle-switch>`
12
+ );
13
+
14
+ expect(el.shadowRoot?.querySelector('#switch-button')).to.exist;
15
+ expect(el.shadowRoot?.querySelector('#knob')).to.exist;
16
+ });
17
+
18
+ it('renders provided L/R values', async () => {
19
+ const el = await fixture<ToggleSwitch>(
20
+ html`<toggle-switch .leftValue=${'L'} .rightValue=${'R'}></toggle-switch>`
21
+ );
22
+
23
+ expect(el.value).to.equal('L');
24
+ expect(el.selectedLabel).to.equal('L');
25
+
26
+ expect(
27
+ el.shadowRoot
28
+ ?.querySelector('label[for=switch-left]')
29
+ ?.textContent?.trim()
30
+ ).to.equal('L');
31
+ expect(
32
+ el.shadowRoot
33
+ ?.querySelector('label[for=switch-right]')
34
+ ?.textContent?.trim()
35
+ ).to.equal('R');
36
+ });
37
+
38
+ it('renders provided L/R labels instead of values', async () => {
39
+ const el = await fixture<ToggleSwitch>(
40
+ html`<toggle-switch
41
+ .leftValue=${'L'}
42
+ .leftLabel=${'L-label'}
43
+ .rightValue=${'R'}
44
+ .rightLabel=${'R-label'}
45
+ ></toggle-switch>`
46
+ );
47
+
48
+ expect(el.value).to.equal('L');
49
+ expect(el.selectedLabel).to.equal('L-label');
50
+
51
+ expect(
52
+ (el.shadowRoot?.querySelector('#switch-left') as HTMLInputElement)?.value
53
+ ).to.equal('L');
54
+ expect(
55
+ (el.shadowRoot?.querySelector('#switch-right') as HTMLInputElement)?.value
56
+ ).to.equal('R');
57
+
58
+ expect(
59
+ el.shadowRoot
60
+ ?.querySelector('label[for=switch-left]')
61
+ ?.textContent?.trim()
62
+ ).to.equal('L-label');
63
+ expect(
64
+ el.shadowRoot
65
+ ?.querySelector('label[for=switch-right]')
66
+ ?.textContent?.trim()
67
+ ).to.equal('R-label');
68
+ });
69
+
70
+ it('sets the initial side', async () => {
71
+ const el = await fixture<ToggleSwitch>(
72
+ html`<toggle-switch
73
+ .leftValue=${'L'}
74
+ .rightValue=${'R'}
75
+ .side=${'right'}
76
+ ></toggle-switch>`
77
+ );
78
+
79
+ expect(el.value).to.equal('R');
80
+ expect(el.selectedLabel).to.equal('R');
81
+ });
82
+
83
+ it('toggles on click', async () => {
84
+ const el = await fixture<ToggleSwitch>(
85
+ html`<toggle-switch .leftValue=${'L'} .rightValue=${'R'}></toggle-switch>`
86
+ );
87
+
88
+ const button = el.shadowRoot?.querySelector(
89
+ '#switch-button'
90
+ ) as HTMLButtonElement;
91
+
92
+ expect(el.value).to.equal('L');
93
+ button.click();
94
+ await el.updateComplete;
95
+
96
+ expect(el.value).to.equal('R');
97
+ button.click();
98
+ await el.updateComplete;
99
+
100
+ expect(el.value).to.equal('L');
101
+ });
102
+
103
+ it('toggles on radio change', async () => {
104
+ const el = await fixture<ToggleSwitch>(
105
+ html`<toggle-switch .leftValue=${'L'} .rightValue=${'R'}></toggle-switch>`
106
+ );
107
+
108
+ const leftRadio = el.shadowRoot?.querySelector(
109
+ '#switch-left'
110
+ ) as HTMLInputElement;
111
+ const rightRadio = el.shadowRoot?.querySelector(
112
+ '#switch-right'
113
+ ) as HTMLInputElement;
114
+
115
+ expect(el.value).to.equal('L');
116
+ rightRadio.click();
117
+ await el.updateComplete;
118
+
119
+ expect(el.value).to.equal('R');
120
+ leftRadio.click();
121
+ await el.updateComplete;
122
+
123
+ expect(el.value).to.equal('L');
124
+ });
125
+
126
+ it('emits change event when toggled', async () => {
127
+ const changeSpy = sinon.spy();
128
+ const el = await fixture<ToggleSwitch>(
129
+ html`<toggle-switch
130
+ .leftValue=${'L'}
131
+ .rightValue=${'R'}
132
+ @change=${changeSpy}
133
+ ></toggle-switch>`
134
+ );
135
+
136
+ const button = el.shadowRoot?.querySelector(
137
+ '#switch-button'
138
+ ) as HTMLButtonElement;
139
+
140
+ button.click();
141
+ await el.updateComplete;
142
+
143
+ expect(changeSpy.callCount).to.equal(1);
144
+
145
+ const leftRadio = el.shadowRoot?.querySelector(
146
+ '#switch-left'
147
+ ) as HTMLInputElement;
148
+
149
+ leftRadio.click();
150
+ await el.updateComplete;
151
+
152
+ expect(changeSpy.callCount).to.equal(2);
153
+ });
154
+ });
@@ -37,6 +37,20 @@ describe('List Tile Compact', () => {
37
37
  expect(creator).to.exist;
38
38
  });
39
39
 
40
+ it('should render title link with model href if provided', async () => {
41
+ const el = await fixture<TileListCompact>(html`
42
+ <tile-list-compact
43
+ .baseNavigationUrl=${''}
44
+ .model=${{ title: 'foo', href: '/foo/bar' }}
45
+ ></tile-list-compact>
46
+ `);
47
+
48
+ const title = el.shadowRoot?.querySelector('#title');
49
+
50
+ expect(title).to.exist;
51
+ expect(title?.getAttribute('href')).to.equal('/foo/bar');
52
+ });
53
+
40
54
  it('should render weekly views when sorting by week', async () => {
41
55
  const el = await fixture<TileListCompact>(html`
42
56
  <tile-list-compact
@@ -37,6 +37,20 @@ describe('List Tile', () => {
37
37
  expect(bottomLine).to.exist;
38
38
  });
39
39
 
40
+ it('should render title link with model href if provided', async () => {
41
+ const el = await fixture<TileList>(html`
42
+ <tile-list
43
+ .baseNavigationUrl=${''}
44
+ .model=${{ title: 'foo', href: '/foo/bar' }}
45
+ ></tile-list>
46
+ `);
47
+
48
+ const title = el.shadowRoot?.querySelector('#title > a');
49
+
50
+ expect(title).to.exist;
51
+ expect(title?.getAttribute('href')).to.equal('/foo/bar');
52
+ });
53
+
40
54
  it('should render with creator element but not dates', async () => {
41
55
  const el = await fixture<TileList>(html`
42
56
  <tile-list .model=${{ creators: ['someone'] }}></tile-list>