@internetarchive/collection-browser 3.3.4-alpha-webdev7761.4 → 3.4.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 (38) hide show
  1. package/dist/src/collection-browser.d.ts +2 -11
  2. package/dist/src/collection-browser.js +693 -856
  3. package/dist/src/collection-browser.js.map +1 -1
  4. package/dist/src/collection-facets/facet-row.js +1 -2
  5. package/dist/src/collection-facets/facet-row.js.map +1 -1
  6. package/dist/src/collection-facets.js +266 -280
  7. package/dist/src/collection-facets.js.map +1 -1
  8. package/dist/src/data-source/collection-browser-data-source-interface.d.ts +1 -10
  9. package/dist/src/data-source/collection-browser-data-source-interface.js.map +1 -1
  10. package/dist/src/data-source/collection-browser-data-source.d.ts +1 -19
  11. package/dist/src/data-source/collection-browser-data-source.js +18 -36
  12. package/dist/src/data-source/collection-browser-data-source.js.map +1 -1
  13. package/dist/src/data-source/collection-browser-query-state.d.ts +2 -1
  14. package/dist/src/data-source/collection-browser-query-state.js.map +1 -1
  15. package/dist/src/data-source/models.d.ts +0 -11
  16. package/dist/src/data-source/models.js.map +1 -1
  17. package/dist/src/models.d.ts +6 -2
  18. package/dist/src/models.js +12 -8
  19. package/dist/src/models.js.map +1 -1
  20. package/dist/src/restoration-state-handler.d.ts +2 -1
  21. package/dist/src/restoration-state-handler.js +9 -3
  22. package/dist/src/restoration-state-handler.js.map +1 -1
  23. package/dist/test/collection-browser.test.js +9 -19
  24. package/dist/test/collection-browser.test.js.map +1 -1
  25. package/dist/test/restoration-state-handler.test.js +37 -5
  26. package/dist/test/restoration-state-handler.test.js.map +1 -1
  27. package/package.json +2 -2
  28. package/src/collection-browser.ts +2829 -3022
  29. package/src/collection-facets/facet-row.ts +1 -4
  30. package/src/collection-facets.ts +995 -1009
  31. package/src/data-source/collection-browser-data-source-interface.ts +0 -12
  32. package/src/data-source/collection-browser-data-source.ts +19 -59
  33. package/src/data-source/collection-browser-query-state.ts +7 -1
  34. package/src/data-source/models.ts +0 -13
  35. package/src/models.ts +14 -10
  36. package/src/restoration-state-handler.ts +11 -9
  37. package/test/collection-browser.test.ts +11 -21
  38. package/test/restoration-state-handler.test.ts +42 -12
@@ -16,7 +16,6 @@ import type {
16
16
  PageSpecifierParams,
17
17
  CollectionTitles,
18
18
  TVChannelAliases,
19
- TVChannelMaps,
20
19
  } from './models';
21
20
 
22
21
  export interface CollectionBrowserDataSourceInterface
@@ -99,11 +98,6 @@ export interface CollectionBrowserDataSourceInterface
99
98
  */
100
99
  readonly tvChannelAliases: TVChannelAliases;
101
100
 
102
- /**
103
- * An object holding mappings from channels to networks, and programs to channels.
104
- */
105
- readonly tvChannelMaps: TVChannelMaps;
106
-
107
101
  /**
108
102
  * The "extra info" package provided by the PPS for collection pages, including details
109
103
  * used to populate the target collection header & About tab content.
@@ -336,10 +330,4 @@ export interface CollectionBrowserDataSourceInterface
336
330
  * of the data source to account for any new gaps in the data.
337
331
  */
338
332
  removeCheckedTiles(): void;
339
-
340
- /**
341
- * Fires requests to populate the TV channel mappings (chan2network and programs2chan),
342
- * or returns the existing Promise for any such request that has already been made.
343
- */
344
- populateTVChannelMaps(): Promise<TVChannelMaps>;
345
333
  }
@@ -24,11 +24,7 @@ import {
24
24
  SORT_OPTIONS,
25
25
  HitRequestSource,
26
26
  } from '../models';
27
- import {
28
- FACETLESS_PAGE_ELEMENTS,
29
- TVChannelMaps,
30
- type PageSpecifierParams,
31
- } from './models';
27
+ import { FACETLESS_PAGE_ELEMENTS, type PageSpecifierParams } from './models';
32
28
  import type { CollectionBrowserDataSourceInterface } from './collection-browser-data-source-interface';
33
29
  import type { CollectionBrowserSearchInterface } from './collection-browser-query-state';
34
30
  import { sha1 } from '../utils/sha1';
@@ -125,11 +121,6 @@ export class CollectionBrowserDataSource
125
121
  */
126
122
  collectionTitles = new Map<string, string>();
127
123
 
128
- /**
129
- * @inheritdoc
130
- */
131
- tvChannelMaps: TVChannelMaps = {};
132
-
133
124
  /**
134
125
  * @inheritdoc
135
126
  */
@@ -171,11 +162,6 @@ export class CollectionBrowserDataSource
171
162
  */
172
163
  queryErrorMessage?: string;
173
164
 
174
- /**
175
- * Internal property to store the promise for any current TV channel maps fetch.
176
- */
177
- private _tvMapsPromise?: Promise<TVChannelMaps>;
178
-
179
165
  /**
180
166
  * Internal property to store the private value backing the `initialSearchComplete` getter.
181
167
  */
@@ -787,6 +773,24 @@ export class CollectionBrowserDataSource
787
773
  );
788
774
  }
789
775
 
776
+ // Add any TV clip type filter, if applicable
777
+ if (this.host.searchType === SearchType.TV) {
778
+ switch (this.host.tvClipFilter) {
779
+ case 'commercials':
780
+ builder.addFilter('ad_id', '*', FilterConstraint.INCLUDE);
781
+ break;
782
+ case 'factchecks':
783
+ builder.addFilter('factcheck', '*', FilterConstraint.INCLUDE);
784
+ break;
785
+ case 'quotes':
786
+ builder.addFilter('clip', '1', FilterConstraint.INCLUDE);
787
+ break;
788
+ case 'all':
789
+ default:
790
+ break;
791
+ }
792
+ }
793
+
790
794
  const filterMap = builder.build();
791
795
  return filterMap;
792
796
  }
@@ -1394,48 +1398,4 @@ export class CollectionBrowserDataSource
1394
1398
  this.updatePrefixFiltersForCurrentSort();
1395
1399
  this.requestHostUpdate();
1396
1400
  }
1397
-
1398
- /**
1399
- * @inheritdoc
1400
- */
1401
- populateTVChannelMaps(): Promise<TVChannelMaps> {
1402
- // To ensure that we only make these requests once, cache the Promise returned by the
1403
- // first call, and return the same Promise on repeated calls.
1404
- // Resolves once both maps have been retrieved and saved in the data source.
1405
- if (!this._tvMapsPromise) {
1406
- this._tvMapsPromise = this._fetchTVChannelMaps();
1407
- }
1408
-
1409
- return this._tvMapsPromise;
1410
- }
1411
-
1412
- /**
1413
- * Internal function implementing the actual fetches for TV channel mappings.
1414
- * This should only called by the public populateTVChannelMaps method, which is guarded so
1415
- * that we do not make extra requests for these rather large mappings.
1416
- */
1417
- private async _fetchTVChannelMaps(): Promise<TVChannelMaps> {
1418
- const baseURL = 'https://av.archive.org/etc';
1419
- const dateStr = new Date().toISOString().slice(0, 10); // YYYY-MM-DD
1420
- const chan2networkPromise = fetch(
1421
- `${baseURL}/chan2network.json?date=${dateStr}`,
1422
- );
1423
- const program2chansPromise = fetch(
1424
- `${baseURL}/program2chans.json?date=${dateStr}`,
1425
- );
1426
-
1427
- const [chan2networkResponse, program2chansResponse] = await Promise.all([
1428
- chan2networkPromise,
1429
- program2chansPromise,
1430
- ]);
1431
- this.tvChannelMaps.channelToNetwork = new Map(
1432
- Object.entries(await chan2networkResponse.json()),
1433
- );
1434
- this.tvChannelMaps.programToChannels = new Map(
1435
- Object.entries(await program2chansResponse.json()),
1436
- );
1437
-
1438
- this.requestHostUpdate();
1439
- return this.tvChannelMaps;
1440
- }
1441
1401
  }
@@ -6,7 +6,12 @@ import type {
6
6
  SortDirection,
7
7
  SortParam,
8
8
  } from '@internetarchive/search-service';
9
- import type { FacetLoadStrategy, SelectedFacets, SortField } from '../models';
9
+ import type {
10
+ FacetLoadStrategy,
11
+ SelectedFacets,
12
+ SortField,
13
+ TvClipFilterType,
14
+ } from '../models';
10
15
  import type { CollectionBrowserDataSourceInterface } from './collection-browser-data-source-interface';
11
16
 
12
17
  /**
@@ -25,6 +30,7 @@ export interface CollectionBrowserQueryState {
25
30
  maxSelectedDate?: string;
26
31
  selectedTitleFilter: string | null;
27
32
  selectedCreatorFilter: string | null;
33
+ tvClipFilter?: TvClipFilterType;
28
34
  selectedSort?: SortField;
29
35
  sortDirection: SortDirection | null;
30
36
  }
@@ -13,19 +13,6 @@ export type CollectionTitles = Map<string, string>;
13
13
  */
14
14
  export type TVChannelAliases = Map<string, string>;
15
15
 
16
- /**
17
- * A Map from program (show) names to a mapping of all the channels that run them.
18
- */
19
- export type TVProgramChannels = Map<string, Record<string, number>>;
20
-
21
- /**
22
- * Object storing the different TV channel mappings used by the data source.
23
- */
24
- export type TVChannelMaps = {
25
- channelToNetwork?: TVChannelAliases;
26
- programToChannels?: TVProgramChannels;
27
- };
28
-
29
16
  /**
30
17
  * The subset of search service params that uniquely specify the type of results
31
18
  * that are sought by an instance of collection browser.
package/src/models.ts CHANGED
@@ -636,7 +636,6 @@ export type FacetOption =
636
636
  | 'collection'
637
637
  | 'year'
638
638
  // TV-specific facet options:
639
- | 'clip_type'
640
639
  | 'program'
641
640
  | 'person'
642
641
  | 'sponsor';
@@ -694,7 +693,6 @@ export const getDefaultSelectedFacets = (): Required<SelectedFacets> => ({
694
693
  creator: {},
695
694
  collection: {},
696
695
  year: {},
697
- clip_type: {},
698
696
  program: {},
699
697
  person: {},
700
698
  sponsor: {},
@@ -703,15 +701,25 @@ export const getDefaultSelectedFacets = (): Required<SelectedFacets> => ({
703
701
  /**
704
702
  * For TV search results, what types of TV clips to restrict the results to.
705
703
  */
706
- export type TvClipFilterType = 'commercial' | 'fact check' | 'quote';
704
+ export type TvClipFilterType = 'all' | 'commercials' | 'factchecks' | 'quotes';
705
+
706
+ /**
707
+ * Map from TV clip filter types to their corresponding URL params
708
+ */
709
+ export const tvClipFiltersToURLParams: Record<TvClipFilterType, string> = {
710
+ all: '',
711
+ commercials: 'only_commercials',
712
+ factchecks: 'only_factchecks',
713
+ quotes: 'only_quotes',
714
+ };
707
715
 
708
716
  /**
709
717
  * Map from allowed TV filtering parameters in the URL to their corresponding filter type
710
718
  */
711
719
  export const tvClipURLParamsToFilters: Record<string, TvClipFilterType> = {
712
- only_commercials: 'commercial',
713
- only_factchecks: 'fact check',
714
- only_quotes: 'quote',
720
+ only_commercials: 'commercials',
721
+ only_factchecks: 'factchecks',
722
+ only_quotes: 'quotes',
715
723
  };
716
724
 
717
725
  /**
@@ -731,7 +739,6 @@ export const defaultFacetDisplayOrder: FacetOption[] = [
731
739
  * Specialized facet ordering when displaying TV search results
732
740
  */
733
741
  export const tvFacetDisplayOrder: FacetOption[] = [
734
- 'clip_type',
735
742
  'program',
736
743
  'creator',
737
744
  'year',
@@ -753,7 +760,6 @@ export const facetTitles: Record<FacetOption, string> = {
753
760
  creator: 'Creator',
754
761
  collection: 'Collection',
755
762
  year: 'Year',
756
- clip_type: 'Clip Type',
757
763
  program: 'Program',
758
764
  person: 'Person',
759
765
  sponsor: 'Sponsor',
@@ -770,7 +776,6 @@ export const defaultFacetSort: Record<FacetOption, AggregationSortType> = {
770
776
  creator: AggregationSortType.COUNT,
771
777
  collection: AggregationSortType.COUNT,
772
778
  year: AggregationSortType.NUMERIC, // Year facets are ordered by their numeric value by default
773
- clip_type: AggregationSortType.COUNT,
774
779
  program: AggregationSortType.COUNT,
775
780
  person: AggregationSortType.COUNT,
776
781
  sponsor: AggregationSortType.COUNT,
@@ -788,7 +793,6 @@ export const valueFacetSort: Record<FacetOption, AggregationSortType> = {
788
793
  creator: AggregationSortType.ALPHABETICAL,
789
794
  collection: AggregationSortType.ALPHABETICAL,
790
795
  year: AggregationSortType.NUMERIC, // Year facets' values should be compared numerically, not lexicographically (year 2001 > year 3)
791
- clip_type: AggregationSortType.ALPHABETICAL,
792
796
  program: AggregationSortType.ALPHABETICAL,
793
797
  person: AggregationSortType.ALPHABETICAL,
794
798
  sponsor: AggregationSortType.ALPHABETICAL,
@@ -5,12 +5,14 @@ import {
5
5
  CollectionBrowserContext,
6
6
  CollectionDisplayMode,
7
7
  SelectedFacets,
8
+ TvClipFilterType,
8
9
  SortField,
9
10
  FacetBucket,
10
11
  FacetState,
11
12
  getDefaultSelectedFacets,
12
13
  sortOptionFromAPIString,
13
14
  SORT_OPTIONS,
15
+ tvClipFiltersToURLParams,
14
16
  tvClipURLParamsToFilters,
15
17
  } from './models';
16
18
  import { arrayEquals } from './utils/array-equals';
@@ -29,6 +31,7 @@ export interface RestorationState {
29
31
  maxSelectedDate?: string;
30
32
  selectedTitleFilter?: string;
31
33
  selectedCreatorFilter?: string;
34
+ tvClipFilter?: TvClipFilterType;
32
35
  }
33
36
 
34
37
  export interface RestorationStatePersistOptions {
@@ -207,6 +210,12 @@ export class RestorationStateHandler
207
210
  newParams.append('and[]', state.creatorQuery);
208
211
  }
209
212
 
213
+ // TV clip special filters
214
+ if (state.tvClipFilter) {
215
+ const tvClipParam = tvClipFiltersToURLParams[state.tvClipFilter];
216
+ if (tvClipParam) newParams.append(tvClipParam, '1');
217
+ }
218
+
210
219
  // Ensure we aren't pushing consecutive identical states to the history stack.
211
220
  // - If the state has changed, we push a new history entry.
212
221
  // - If only the page number has changed, we replace the current history entry.
@@ -403,16 +412,9 @@ export class RestorationStateHandler
403
412
  }
404
413
 
405
414
  // TV clip special filters (carryovers from legacy page)
406
- for (const [paramKey, facetKey] of Object.entries(
407
- tvClipURLParamsToFilters,
408
- )) {
415
+ for (const [paramKey, filter] of Object.entries(tvClipURLParamsToFilters)) {
409
416
  if (url.searchParams.get(paramKey)) {
410
- this.setSelectedFacetState(
411
- restorationState.selectedFacets,
412
- 'clip_type',
413
- facetKey,
414
- 'selected',
415
- );
417
+ restorationState.tvClipFilter = filter;
416
418
  break;
417
419
  }
418
420
  }
@@ -1087,7 +1087,7 @@ describe('Collection Browser', () => {
1087
1087
  });
1088
1088
  });
1089
1089
 
1090
- it('applies correct TV search filter for commercials', async () => {
1090
+ it('applies correct search filter when TV clip filter set to commercials', async () => {
1091
1091
  const searchService = new MockSearchService();
1092
1092
  const el = await fixture<CollectionBrowser>(
1093
1093
  html`<collection-browser .searchService=${searchService}>
@@ -1096,20 +1096,16 @@ describe('Collection Browser', () => {
1096
1096
 
1097
1097
  el.baseQuery = 'tv-fields';
1098
1098
  el.searchType = SearchType.TV;
1099
- el.selectedFacets = {
1100
- clip_type: {
1101
- commercial: { key: 'commercial', count: 1, state: 'selected' },
1102
- },
1103
- };
1099
+ el.tvClipFilter = 'commercials';
1104
1100
  await el.updateComplete;
1105
1101
  await el.initialSearchComplete;
1106
1102
 
1107
- expect(searchService.searchParams?.filters?.clip_type?.commercial).to.equal(
1103
+ expect(searchService.searchParams?.filters?.ad_id?.['*']).to.equal(
1108
1104
  FilterConstraint.INCLUDE,
1109
1105
  );
1110
1106
  });
1111
1107
 
1112
- it('applies correct TV search filter for fact checks', async () => {
1108
+ it('applies correct search filter when TV clip filter set to factchecks', async () => {
1113
1109
  const searchService = new MockSearchService();
1114
1110
  const el = await fixture<CollectionBrowser>(
1115
1111
  html`<collection-browser .searchService=${searchService}>
@@ -1118,20 +1114,16 @@ describe('Collection Browser', () => {
1118
1114
 
1119
1115
  el.baseQuery = 'tv-fields';
1120
1116
  el.searchType = SearchType.TV;
1121
- el.selectedFacets = {
1122
- clip_type: {
1123
- 'fact check': { key: 'fact check', count: 1, state: 'selected' },
1124
- },
1125
- };
1117
+ el.tvClipFilter = 'factchecks';
1126
1118
  await el.updateComplete;
1127
1119
  await el.initialSearchComplete;
1128
1120
 
1129
- expect(
1130
- searchService.searchParams?.filters?.clip_type?.['fact check'],
1131
- ).to.equal(FilterConstraint.INCLUDE);
1121
+ expect(searchService.searchParams?.filters?.factcheck?.['*']).to.equal(
1122
+ FilterConstraint.INCLUDE,
1123
+ );
1132
1124
  });
1133
1125
 
1134
- it('applies correct TV search filter for quotes', async () => {
1126
+ it('applies correct search filter when TV clip filter set to quotes', async () => {
1135
1127
  const searchService = new MockSearchService();
1136
1128
  const el = await fixture<CollectionBrowser>(
1137
1129
  html`<collection-browser .searchService=${searchService}>
@@ -1140,13 +1132,11 @@ describe('Collection Browser', () => {
1140
1132
 
1141
1133
  el.baseQuery = 'tv-fields';
1142
1134
  el.searchType = SearchType.TV;
1143
- el.selectedFacets = {
1144
- clip_type: { quote: { key: 'quote', count: 1, state: 'selected' } },
1145
- };
1135
+ el.tvClipFilter = 'quotes';
1146
1136
  await el.updateComplete;
1147
1137
  await el.initialSearchComplete;
1148
1138
 
1149
- expect(searchService.searchParams?.filters?.clip_type?.quote).to.equal(
1139
+ expect(searchService.searchParams?.filters?.clip?.['1']).to.equal(
1150
1140
  FilterConstraint.INCLUDE,
1151
1141
  );
1152
1142
  });
@@ -250,33 +250,25 @@ describe('Restoration state handler', () => {
250
250
  url.search = '?only_commercials=1';
251
251
  window.history.replaceState({ path: url.href }, '', url.href);
252
252
  const commercialsRestorationState = handler.getRestorationState();
253
- expect(
254
- commercialsRestorationState.selectedFacets.clip_type?.commercial.state,
255
- ).to.equal('selected');
253
+ expect(commercialsRestorationState.tvClipFilter).to.equal('commercials');
256
254
 
257
255
  // Fact checks
258
256
  url.search = '?only_factchecks=1';
259
257
  window.history.replaceState({ path: url.href }, '', url.href);
260
258
  const factchecksRestorationState = handler.getRestorationState();
261
- expect(
262
- factchecksRestorationState.selectedFacets.clip_type?.['fact check'].state,
263
- ).to.equal('selected');
259
+ expect(factchecksRestorationState.tvClipFilter).to.equal('factchecks');
264
260
 
265
261
  // Quotes
266
262
  url.search = '?only_quotes=1';
267
263
  window.history.replaceState({ path: url.href }, '', url.href);
268
264
  const quotesRestorationState = handler.getRestorationState();
269
- expect(
270
- quotesRestorationState.selectedFacets.clip_type?.quote.state,
271
- ).to.equal('selected');
265
+ expect(quotesRestorationState.tvClipFilter).to.equal('quotes');
272
266
 
273
267
  // No filter param
274
268
  url.search = '';
275
269
  window.history.replaceState({ path: url.href }, '', url.href);
276
270
  const unfilteredRestorationState = handler.getRestorationState();
277
- expect(unfilteredRestorationState.selectedFacets.clip_type).to.deep.equal(
278
- {},
279
- );
271
+ expect(unfilteredRestorationState.tvClipFilter).not.to.exist;
280
272
  });
281
273
 
282
274
  it('should restore sort from URL (space format)', async () => {
@@ -410,6 +402,44 @@ describe('Restoration state handler', () => {
410
402
  expect(window.location.search).to.equal('?page=2');
411
403
  });
412
404
 
405
+ it('should persist TV clip filter types to the URL', async () => {
406
+ const url = new URL(window.location.href);
407
+ url.search = '';
408
+ window.history.replaceState({ path: url.href }, '', url.href);
409
+
410
+ // Commercials
411
+ const handler = new RestorationStateHandler({ context: 'search' });
412
+ handler.persistState({
413
+ tvClipFilter: 'commercials',
414
+ selectedFacets: getDefaultSelectedFacets(),
415
+ });
416
+ expect(window.location.search).to.equal('?only_commercials=1');
417
+
418
+ // Fact checks
419
+ window.history.replaceState({ path: url.href }, '', url.href);
420
+ handler.persistState({
421
+ tvClipFilter: 'factchecks',
422
+ selectedFacets: getDefaultSelectedFacets(),
423
+ });
424
+ expect(window.location.search).to.equal('?only_factchecks=1');
425
+
426
+ // Quotes
427
+ window.history.replaceState({ path: url.href }, '', url.href);
428
+ handler.persistState({
429
+ tvClipFilter: 'quotes',
430
+ selectedFacets: getDefaultSelectedFacets(),
431
+ });
432
+ expect(window.location.search).to.equal('?only_quotes=1');
433
+
434
+ // Unfiltered
435
+ window.history.replaceState({ path: url.href }, '', url.href);
436
+ handler.persistState({
437
+ tvClipFilter: 'all',
438
+ selectedFacets: getDefaultSelectedFacets(),
439
+ });
440
+ expect(window.location.search).to.equal('');
441
+ });
442
+
413
443
  it('should upgrade legacy search params to new ones', async () => {
414
444
  const url = new URL(window.location.href);
415
445
  url.search = '?q=foo';