@internetarchive/collection-browser 2.13.2-alpha-webdev7687.5 → 2.13.2-alpha-webdev7687.7

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 (31) hide show
  1. package/dist/src/collection-browser.d.ts +1 -1
  2. package/dist/src/collection-browser.js +2 -2
  3. package/dist/src/collection-browser.js.map +1 -1
  4. package/dist/src/models.d.ts +8 -0
  5. package/dist/src/models.js +17 -0
  6. package/dist/src/models.js.map +1 -1
  7. package/dist/src/restoration-state-handler.js +13 -27
  8. package/dist/src/restoration-state-handler.js.map +1 -1
  9. package/dist/src/tiles/grid/tile-stats.js +1 -6
  10. package/dist/src/tiles/grid/tile-stats.js.map +1 -1
  11. package/dist/src/tiles/list/tile-list-compact.js +3 -9
  12. package/dist/src/tiles/list/tile-list-compact.js.map +1 -1
  13. package/dist/src/tiles/list/tile-list.js +1 -8
  14. package/dist/src/tiles/list/tile-list.js.map +1 -1
  15. package/dist/src/tiles/tile-mediatype-icon.d.ts +1 -5
  16. package/dist/src/tiles/tile-mediatype-icon.js +19 -26
  17. package/dist/src/tiles/tile-mediatype-icon.js.map +1 -1
  18. package/dist/test/restoration-state-handler.test.js +58 -1
  19. package/dist/test/restoration-state-handler.test.js.map +1 -1
  20. package/dist/test/tiles/tile-mediatype-icon.test.js +83 -26
  21. package/dist/test/tiles/tile-mediatype-icon.test.js.map +1 -1
  22. package/package.json +1 -1
  23. package/src/collection-browser.ts +2 -2
  24. package/src/models.ts +19 -0
  25. package/src/restoration-state-handler.ts +13 -23
  26. package/src/tiles/grid/tile-stats.ts +1 -6
  27. package/src/tiles/list/tile-list-compact.ts +1 -7
  28. package/src/tiles/list/tile-list.ts +1 -7
  29. package/src/tiles/tile-mediatype-icon.ts +20 -20
  30. package/test/restoration-state-handler.test.ts +71 -1
  31. package/test/tiles/tile-mediatype-icon.test.ts +89 -26
@@ -5,7 +5,7 @@ import {
5
5
  mediatypeConfig,
6
6
  MediatypeConfigKey,
7
7
  } from '../mediatype/mediatype-config';
8
- import { TileModel } from '../models';
8
+ import type { TileModel } from '../models';
9
9
 
10
10
  const TV_COMMERCIAL_COLLECTION = 'tv_ads';
11
11
  const TV_FACT_CHECK_COLLECTION = 'factchecked';
@@ -14,14 +14,8 @@ const RADIO_COLLECTIONS = new Set(['radio', 'radioprogram']);
14
14
 
15
15
  @customElement('tile-mediatype-icon')
16
16
  export class TileMediatypeIcon extends LitElement {
17
- @property({ type: String }) mediatype?: MediatypeConfigKey;
18
-
19
17
  @property({ type: Object }) model?: TileModel;
20
18
 
21
- @property({ type: Array }) collections?: string[];
22
-
23
- @property({ type: Boolean }) isTvSearchResult = false;
24
-
25
19
  @property({ type: Boolean }) showText = false;
26
20
 
27
21
  /**
@@ -30,7 +24,7 @@ export class TileMediatypeIcon extends LitElement {
30
24
  private get displayMediatype(): MediatypeConfigKey {
31
25
  if (this.isTvItem) return this.tvDisplayMediatype;
32
26
  if (this.isRadioItem) return 'radio';
33
- return this.mediatype ?? 'none';
27
+ return this.model?.mediatype ?? 'none';
34
28
  }
35
29
 
36
30
  /**
@@ -39,9 +33,9 @@ export class TileMediatypeIcon extends LitElement {
39
33
  private get tvDisplayMediatype(): MediatypeConfigKey {
40
34
  if (this.isTvCommercial) {
41
35
  return 'tvCommercial';
42
- } else if (this.isTvSearchResult && this.isTvFactCheck) {
36
+ } else if (this.model?.isTvSearchResult && this.isTvFactCheck) {
43
37
  return 'tvFactCheck';
44
- } else if (this.isTvSearchResult && this.isTvQuote) {
38
+ } else if (this.model?.isTvSearchResult && this.isTvQuote) {
45
39
  return 'tvQuote';
46
40
  }
47
41
 
@@ -52,20 +46,26 @@ export class TileMediatypeIcon extends LitElement {
52
46
  * Whether this represents a TV item
53
47
  */
54
48
  private get isTvItem(): boolean {
55
- return (
56
- this.mediatype === 'movies' &&
57
- !!this.collections?.some(id => TV_COLLECTIONS.has(id))
49
+ return !!(
50
+ this.model?.mediatype === 'movies' &&
51
+ this.model?.collections.some(id => TV_COLLECTIONS.has(id))
58
52
  );
59
53
  }
60
54
 
61
55
  private get isTvCommercial(): boolean {
62
- // Contains one or more TV ad identifiers
63
- return !!this.model?.adIds?.length;
56
+ // Contains one or more TV ad identifiers or is in the tv_ads collection
57
+ return !!(
58
+ this.model?.adIds?.length ||
59
+ this.model?.collections.includes(TV_COMMERCIAL_COLLECTION)
60
+ );
64
61
  }
65
62
 
66
63
  private get isTvFactCheck(): boolean {
67
- // Contains one or more fact-check URLs
68
- return !!this.model?.factChecks?.length;
64
+ // Contains one or more fact-check URLs or is in the factchecked collection
65
+ return !!(
66
+ this.model?.factChecks?.length ||
67
+ this.model?.collections.includes(TV_FACT_CHECK_COLLECTION)
68
+ );
69
69
  }
70
70
 
71
71
  private get isTvQuote(): boolean {
@@ -76,9 +76,9 @@ export class TileMediatypeIcon extends LitElement {
76
76
  * Whether this represents a radio item
77
77
  */
78
78
  private get isRadioItem(): boolean {
79
- return (
80
- this.mediatype === 'audio' &&
81
- !!this.collections?.some(id => RADIO_COLLECTIONS.has(id))
79
+ return !!(
80
+ this.model?.mediatype === 'audio' &&
81
+ this.model?.collections.some(id => RADIO_COLLECTIONS.has(id))
82
82
  );
83
83
  }
84
84
 
@@ -1,7 +1,10 @@
1
1
  import { SearchType } from '@internetarchive/search-service';
2
2
  import { expect } from '@open-wc/testing';
3
3
  import { SortField, getDefaultSelectedFacets } from '../src/models';
4
- import { RestorationStateHandler } from '../src/restoration-state-handler';
4
+ import {
5
+ RestorationState,
6
+ RestorationStateHandler,
7
+ } from '../src/restoration-state-handler';
5
8
 
6
9
  describe('Restoration state handler', () => {
7
10
  it('should restore query from URL', async () => {
@@ -242,6 +245,35 @@ describe('Restoration state handler', () => {
242
245
  );
243
246
  });
244
247
 
248
+ it('should restore any TV clip filters from URL', async () => {
249
+ const handler = new RestorationStateHandler({ context: 'search' });
250
+ const url = new URL(window.location.href);
251
+
252
+ // Commercials
253
+ url.search = '?only_commercials=1';
254
+ window.history.replaceState({ path: url.href }, '', url.href);
255
+ const commercialsRestorationState = handler.getRestorationState();
256
+ expect(commercialsRestorationState.tvClipFilter).to.equal('commercials');
257
+
258
+ // Fact checks
259
+ url.search = '?only_factchecks=1';
260
+ window.history.replaceState({ path: url.href }, '', url.href);
261
+ const factchecksRestorationState = handler.getRestorationState();
262
+ expect(factchecksRestorationState.tvClipFilter).to.equal('factchecks');
263
+
264
+ // Quotes
265
+ url.search = '?only_quotes=1';
266
+ window.history.replaceState({ path: url.href }, '', url.href);
267
+ const quotesRestorationState = handler.getRestorationState();
268
+ expect(quotesRestorationState.tvClipFilter).to.equal('quotes');
269
+
270
+ // No filter param
271
+ url.search = '';
272
+ window.history.replaceState({ path: url.href }, '', url.href);
273
+ const unfilteredRestorationState = handler.getRestorationState();
274
+ expect(unfilteredRestorationState.tvClipFilter).not.to.exist;
275
+ });
276
+
245
277
  it('should restore sort from URL (space format)', async () => {
246
278
  const handler = new RestorationStateHandler({ context: 'search' });
247
279
 
@@ -373,6 +405,44 @@ describe('Restoration state handler', () => {
373
405
  expect(window.location.search).to.equal('?page=2');
374
406
  });
375
407
 
408
+ it('should persist TV clip filter types to the URL', async () => {
409
+ const url = new URL(window.location.href);
410
+ url.search = '';
411
+ window.history.replaceState({ path: url.href }, '', url.href);
412
+
413
+ // Commercials
414
+ const handler = new RestorationStateHandler({ context: 'search' });
415
+ handler.persistState({
416
+ tvClipFilter: 'commercials',
417
+ selectedFacets: getDefaultSelectedFacets(),
418
+ });
419
+ expect(window.location.search).to.equal('?only_commercials=1');
420
+
421
+ // Fact checks
422
+ window.history.replaceState({ path: url.href }, '', url.href);
423
+ handler.persistState({
424
+ tvClipFilter: 'factchecks',
425
+ selectedFacets: getDefaultSelectedFacets(),
426
+ });
427
+ expect(window.location.search).to.equal('?only_factchecks=1');
428
+
429
+ // Quotes
430
+ window.history.replaceState({ path: url.href }, '', url.href);
431
+ handler.persistState({
432
+ tvClipFilter: 'quotes',
433
+ selectedFacets: getDefaultSelectedFacets(),
434
+ });
435
+ expect(window.location.search).to.equal('?only_quotes=1');
436
+
437
+ // Unfiltered
438
+ window.history.replaceState({ path: url.href }, '', url.href);
439
+ handler.persistState({
440
+ tvClipFilter: 'all',
441
+ selectedFacets: getDefaultSelectedFacets(),
442
+ });
443
+ expect(window.location.search).to.equal('');
444
+ });
445
+
376
446
  it('should upgrade legacy search params to new ones', async () => {
377
447
  const url = new URL(window.location.href);
378
448
  url.search = '?q=foo';
@@ -3,11 +3,19 @@ import { html } from 'lit';
3
3
  import type { TileMediatypeIcon } from '../../src/tiles/tile-mediatype-icon';
4
4
 
5
5
  import '../../src/tiles/tile-mediatype-icon';
6
+ import { TileModel } from '../../src/models';
7
+ import { MediaType } from '@internetarchive/field-parsers';
6
8
 
7
9
  describe('Mediatype Icon', () => {
10
+ let model: TileModel;
11
+ beforeEach(() => {
12
+ model = new TileModel({});
13
+ });
14
+
8
15
  it('renders component', async () => {
16
+ model.mediatype = 'texts';
9
17
  const el = await fixture<TileMediatypeIcon>(html`
10
- <tile-mediatype-icon mediatype="texts"></tile-mediatype-icon>
18
+ <tile-mediatype-icon .model=${model}></tile-mediatype-icon>
11
19
  `);
12
20
 
13
21
  const iconDiv = el.shadowRoot?.querySelector('#icon');
@@ -15,8 +23,9 @@ describe('Mediatype Icon', () => {
15
23
  });
16
24
 
17
25
  it('renders basic mediatype correctly', async () => {
26
+ model.mediatype = 'movies';
18
27
  const el = await fixture<TileMediatypeIcon>(html`
19
- <tile-mediatype-icon mediatype="movies"></tile-mediatype-icon>
28
+ <tile-mediatype-icon .model=${model}></tile-mediatype-icon>
20
29
  `);
21
30
 
22
31
  const iconDiv = el.shadowRoot?.querySelector('#icon') as HTMLDivElement;
@@ -26,36 +35,60 @@ describe('Mediatype Icon', () => {
26
35
  });
27
36
 
28
37
  it('renders TV mediatype', async () => {
38
+ model.mediatype = 'movies';
39
+ model.collections = ['tvnews'];
29
40
  const el = await fixture<TileMediatypeIcon>(html`
30
- <tile-mediatype-icon
31
- mediatype="movies"
32
- .collections=${['tvnews']}
33
- ></tile-mediatype-icon>
41
+ <tile-mediatype-icon .model=${model}></tile-mediatype-icon>
34
42
  `);
35
43
 
36
44
  const iconDiv = el.shadowRoot?.querySelector('#icon') as HTMLDivElement;
37
45
  expect(iconDiv.title).to.equal('TV');
38
46
  });
39
47
 
40
- it('renders TV Commercial mediatype', async () => {
48
+ it('renders TV Commercial mediatype for TV items with ad ids', async () => {
49
+ model.mediatype = 'movies';
50
+ model.collections = ['tvnews'];
51
+ model.adIds = ['foo'];
41
52
  const el = await fixture<TileMediatypeIcon>(html`
42
- <tile-mediatype-icon
43
- mediatype="movies"
44
- .collections=${['tvnews', 'tv_ads']}
45
- ></tile-mediatype-icon>
53
+ <tile-mediatype-icon .model=${model}></tile-mediatype-icon>
46
54
  `);
47
55
 
48
56
  const iconDiv = el.shadowRoot?.querySelector('#icon') as HTMLDivElement;
49
57
  expect(iconDiv.title).to.equal('TV Commercial');
50
58
  });
51
59
 
52
- it('renders TV Fact Check mediatype for search results', async () => {
60
+ it('renders TV Commercial mediatype for TV items in tv_ads collection', async () => {
61
+ model.mediatype = 'movies';
62
+ model.collections = ['tvnews', 'tv_ads'];
53
63
  const el = await fixture<TileMediatypeIcon>(html`
54
- <tile-mediatype-icon
55
- isTvSearchResult
56
- mediatype="movies"
57
- .collections=${['tvnews', 'factchecked']}
58
- ></tile-mediatype-icon>
64
+ <tile-mediatype-icon .model=${model}></tile-mediatype-icon>
65
+ `);
66
+
67
+ const iconDiv = el.shadowRoot?.querySelector('#icon') as HTMLDivElement;
68
+ expect(iconDiv.title).to.equal('TV Commercial');
69
+ });
70
+
71
+ it('renders TV Fact Check mediatype for TV search results with fact check URLs', async () => {
72
+ model.hitType = 'tv_clip';
73
+ model.hitRequestSource = 'search_query';
74
+ model.mediatype = 'movies';
75
+ model.collections = ['tvnews'];
76
+ model.factChecks = ['https://example.com'];
77
+ const el = await fixture<TileMediatypeIcon>(html`
78
+ <tile-mediatype-icon .model=${model}></tile-mediatype-icon>
79
+ `);
80
+
81
+ const iconDiv = el.shadowRoot?.querySelector('#icon') as HTMLDivElement;
82
+ expect(iconDiv.title).to.equal('TV Fact Check');
83
+ });
84
+
85
+ it('renders TV Fact Check mediatype for TV search results in factchecked collection', async () => {
86
+ model.hitType = 'tv_clip';
87
+ model.hitRequestSource = 'search_query';
88
+ model.mediatype = 'movies';
89
+ model.collections = ['tvnews', 'factchecked'];
90
+ const el = await fixture<TileMediatypeIcon>(html`
91
+ <tile-mediatype-icon .model=${model}></tile-mediatype-icon>
59
92
  `);
60
93
 
61
94
  const iconDiv = el.shadowRoot?.querySelector('#icon') as HTMLDivElement;
@@ -63,11 +96,41 @@ describe('Mediatype Icon', () => {
63
96
  });
64
97
 
65
98
  it('does not use TV Fact Check mediatype for non-search results', async () => {
99
+ model.hitType = 'tv_clip';
100
+ model.hitRequestSource = 'collection_members';
101
+ model.mediatype = 'movies';
102
+ model.collections = ['tvnews'];
103
+ model.factChecks = ['https://example.com'];
104
+ const el = await fixture<TileMediatypeIcon>(html`
105
+ <tile-mediatype-icon .model=${model}></tile-mediatype-icon>
106
+ `);
107
+
108
+ const iconDiv = el.shadowRoot?.querySelector('#icon') as HTMLDivElement;
109
+ expect(iconDiv.title).to.equal('TV');
110
+ });
111
+
112
+ it('renders TV Quote mediatype for TV search results that are clips', async () => {
113
+ model.hitType = 'tv_clip';
114
+ model.hitRequestSource = 'search_query';
115
+ model.mediatype = 'movies';
116
+ model.collections = ['tvnews'];
117
+ model.isClip = true;
118
+ const el = await fixture<TileMediatypeIcon>(html`
119
+ <tile-mediatype-icon .model=${model}></tile-mediatype-icon>
120
+ `);
121
+
122
+ const iconDiv = el.shadowRoot?.querySelector('#icon') as HTMLDivElement;
123
+ expect(iconDiv.title).to.equal('TV Quote');
124
+ });
125
+
126
+ it('does not use TV Quote mediatype for non-search results', async () => {
127
+ model.hitType = 'tv_clip';
128
+ model.hitRequestSource = 'collection_members';
129
+ model.mediatype = 'movies';
130
+ model.collections = ['tvnews'];
131
+ model.isClip = true;
66
132
  const el = await fixture<TileMediatypeIcon>(html`
67
- <tile-mediatype-icon
68
- mediatype="movies"
69
- .collections=${['tvnews', 'factchecked']}
70
- ></tile-mediatype-icon>
133
+ <tile-mediatype-icon .model=${model}></tile-mediatype-icon>
71
134
  `);
72
135
 
73
136
  const iconDiv = el.shadowRoot?.querySelector('#icon') as HTMLDivElement;
@@ -75,11 +138,10 @@ describe('Mediatype Icon', () => {
75
138
  });
76
139
 
77
140
  it('renders radio mediatype', async () => {
141
+ model.mediatype = 'audio';
142
+ model.collections = ['radio'];
78
143
  const el = await fixture<TileMediatypeIcon>(html`
79
- <tile-mediatype-icon
80
- mediatype="audio"
81
- .collections=${['radio']}
82
- ></tile-mediatype-icon>
144
+ <tile-mediatype-icon .model=${model}></tile-mediatype-icon>
83
145
  `);
84
146
 
85
147
  const iconDiv = el.shadowRoot?.querySelector('#icon') as HTMLDivElement;
@@ -87,8 +149,9 @@ describe('Mediatype Icon', () => {
87
149
  });
88
150
 
89
151
  it('renders no icon if mediatype is unrecognized', async () => {
152
+ model.mediatype = 'foobar' as MediaType;
90
153
  const el = await fixture<TileMediatypeIcon>(html`
91
- <tile-mediatype-icon mediatype="foobar"></tile-mediatype-icon>
154
+ <tile-mediatype-icon .model=${model}></tile-mediatype-icon>
92
155
  `);
93
156
 
94
157
  const iconDiv = el.shadowRoot?.querySelector('#icon');