@internetarchive/collection-browser 0.3.7 → 0.3.8

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 (218) hide show
  1. package/.editorconfig +29 -29
  2. package/.github/workflows/ci.yml +26 -26
  3. package/.github/workflows/gh-pages-main.yml +39 -39
  4. package/.github/workflows/npm-publish.yml +39 -39
  5. package/.github/workflows/pr-preview.yml +38 -38
  6. package/.husky/pre-commit +4 -4
  7. package/LICENSE +661 -661
  8. package/README.md +83 -83
  9. package/dist/index.d.ts +9 -9
  10. package/dist/index.js +9 -9
  11. package/dist/src/app-root.d.ts +48 -47
  12. package/dist/src/app-root.js +271 -250
  13. package/dist/src/app-root.js.map +1 -1
  14. package/dist/src/assets/img/icons/arrow-left.d.ts +2 -2
  15. package/dist/src/assets/img/icons/arrow-left.js +2 -2
  16. package/dist/src/assets/img/icons/arrow-right.d.ts +2 -2
  17. package/dist/src/assets/img/icons/arrow-right.js +2 -2
  18. package/dist/src/assets/img/icons/chevron.d.ts +2 -2
  19. package/dist/src/assets/img/icons/chevron.js +2 -2
  20. package/dist/src/assets/img/icons/empty-query.d.ts +2 -2
  21. package/dist/src/assets/img/icons/empty-query.js +2 -2
  22. package/dist/src/assets/img/icons/eye-closed.d.ts +2 -2
  23. package/dist/src/assets/img/icons/eye-closed.js +2 -2
  24. package/dist/src/assets/img/icons/eye.d.ts +2 -2
  25. package/dist/src/assets/img/icons/eye.js +2 -2
  26. package/dist/src/assets/img/icons/favorite-filled.d.ts +1 -1
  27. package/dist/src/assets/img/icons/favorite-filled.js +2 -2
  28. package/dist/src/assets/img/icons/login-required.d.ts +1 -1
  29. package/dist/src/assets/img/icons/login-required.js +2 -2
  30. package/dist/src/assets/img/icons/mediatype/account.d.ts +1 -1
  31. package/dist/src/assets/img/icons/mediatype/account.js +2 -2
  32. package/dist/src/assets/img/icons/mediatype/audio.d.ts +1 -1
  33. package/dist/src/assets/img/icons/mediatype/audio.js +2 -2
  34. package/dist/src/assets/img/icons/mediatype/collection.d.ts +1 -1
  35. package/dist/src/assets/img/icons/mediatype/collection.js +2 -2
  36. package/dist/src/assets/img/icons/mediatype/data.d.ts +1 -1
  37. package/dist/src/assets/img/icons/mediatype/data.js +2 -2
  38. package/dist/src/assets/img/icons/mediatype/etree.d.ts +1 -1
  39. package/dist/src/assets/img/icons/mediatype/etree.js +2 -2
  40. package/dist/src/assets/img/icons/mediatype/film.d.ts +1 -1
  41. package/dist/src/assets/img/icons/mediatype/film.js +2 -2
  42. package/dist/src/assets/img/icons/mediatype/images.d.ts +1 -1
  43. package/dist/src/assets/img/icons/mediatype/images.js +2 -2
  44. package/dist/src/assets/img/icons/mediatype/radio.d.ts +1 -1
  45. package/dist/src/assets/img/icons/mediatype/radio.js +2 -2
  46. package/dist/src/assets/img/icons/mediatype/software.d.ts +1 -1
  47. package/dist/src/assets/img/icons/mediatype/software.js +2 -2
  48. package/dist/src/assets/img/icons/mediatype/texts.d.ts +1 -1
  49. package/dist/src/assets/img/icons/mediatype/texts.js +2 -2
  50. package/dist/src/assets/img/icons/mediatype/tv.d.ts +1 -1
  51. package/dist/src/assets/img/icons/mediatype/tv.js +2 -2
  52. package/dist/src/assets/img/icons/mediatype/video.d.ts +1 -1
  53. package/dist/src/assets/img/icons/mediatype/video.js +2 -2
  54. package/dist/src/assets/img/icons/mediatype/web.d.ts +1 -1
  55. package/dist/src/assets/img/icons/mediatype/web.js +2 -2
  56. package/dist/src/assets/img/icons/null-result.d.ts +2 -2
  57. package/dist/src/assets/img/icons/null-result.js +2 -2
  58. package/dist/src/assets/img/icons/restricted.d.ts +1 -1
  59. package/dist/src/assets/img/icons/restricted.js +2 -2
  60. package/dist/src/assets/img/icons/reviews.d.ts +1 -1
  61. package/dist/src/assets/img/icons/reviews.js +2 -2
  62. package/dist/src/assets/img/icons/upload.d.ts +1 -1
  63. package/dist/src/assets/img/icons/upload.js +2 -2
  64. package/dist/src/assets/img/icons/views.d.ts +1 -1
  65. package/dist/src/assets/img/icons/views.js +2 -2
  66. package/dist/src/circular-activity-indicator.d.ts +5 -5
  67. package/dist/src/circular-activity-indicator.js +17 -17
  68. package/dist/src/collection-browser.d.ts +278 -246
  69. package/dist/src/collection-browser.js +1105 -1031
  70. package/dist/src/collection-browser.js.map +1 -1
  71. package/dist/src/collection-facets/facet-tombstone-row.d.ts +5 -5
  72. package/dist/src/collection-facets/facet-tombstone-row.js +15 -15
  73. package/dist/src/collection-facets/facets-template.d.ts +16 -16
  74. package/dist/src/collection-facets/facets-template.js +125 -125
  75. package/dist/src/collection-facets/more-facets-content.d.ts +76 -76
  76. package/dist/src/collection-facets/more-facets-content.js +353 -353
  77. package/dist/src/collection-facets/more-facets-pagination.d.ts +36 -36
  78. package/dist/src/collection-facets/more-facets-pagination.js +192 -192
  79. package/dist/src/collection-facets.d.ts +77 -77
  80. package/dist/src/collection-facets.js +388 -389
  81. package/dist/src/collection-facets.js.map +1 -1
  82. package/dist/src/empty-placeholder.d.ts +11 -11
  83. package/dist/src/empty-placeholder.js +42 -42
  84. package/dist/src/language-code-handler/language-code-handler.d.ts +37 -37
  85. package/dist/src/language-code-handler/language-code-handler.js +26 -26
  86. package/dist/src/language-code-handler/language-code-mapping.d.ts +1 -1
  87. package/dist/src/language-code-handler/language-code-mapping.js +562 -562
  88. package/dist/src/mediatype/mediatype-config.d.ts +3 -3
  89. package/dist/src/mediatype/mediatype-config.js +85 -85
  90. package/dist/src/models.d.ts +103 -103
  91. package/dist/src/models.js +117 -117
  92. package/dist/src/restoration-state-handler.d.ts +46 -46
  93. package/dist/src/restoration-state-handler.js +230 -230
  94. package/dist/src/sort-filter-bar/alpha-bar.d.ts +9 -9
  95. package/dist/src/sort-filter-bar/alpha-bar.js +41 -41
  96. package/dist/src/sort-filter-bar/img/compact.d.ts +1 -1
  97. package/dist/src/sort-filter-bar/img/compact.js +2 -2
  98. package/dist/src/sort-filter-bar/img/list.d.ts +1 -1
  99. package/dist/src/sort-filter-bar/img/list.js +2 -2
  100. package/dist/src/sort-filter-bar/img/sort-triangle.d.ts +1 -1
  101. package/dist/src/sort-filter-bar/img/sort-triangle.js +2 -2
  102. package/dist/src/sort-filter-bar/img/tile.d.ts +1 -1
  103. package/dist/src/sort-filter-bar/img/tile.js +2 -2
  104. package/dist/src/sort-filter-bar/sort-filter-bar.d.ts +107 -107
  105. package/dist/src/sort-filter-bar/sort-filter-bar.js +423 -423
  106. package/dist/src/styles/item-image-styles.d.ts +8 -8
  107. package/dist/src/styles/item-image-styles.js +9 -9
  108. package/dist/src/tiles/collection-browser-loading-tile.d.ts +5 -5
  109. package/dist/src/tiles/collection-browser-loading-tile.js +15 -15
  110. package/dist/src/tiles/grid/account-tile.d.ts +8 -8
  111. package/dist/src/tiles/grid/account-tile.js +20 -20
  112. package/dist/src/tiles/grid/collection-tile.d.ts +7 -7
  113. package/dist/src/tiles/grid/collection-tile.js +23 -23
  114. package/dist/src/tiles/grid/item-tile.d.ts +24 -24
  115. package/dist/src/tiles/grid/item-tile.js +87 -87
  116. package/dist/src/tiles/grid/tile-stats.d.ts +10 -10
  117. package/dist/src/tiles/grid/tile-stats.js +40 -40
  118. package/dist/src/tiles/image-block.d.ts +17 -17
  119. package/dist/src/tiles/image-block.js +69 -69
  120. package/dist/src/tiles/item-image.d.ts +31 -31
  121. package/dist/src/tiles/item-image.js +103 -103
  122. package/dist/src/tiles/list/account-label.d.ts +1 -1
  123. package/dist/src/tiles/list/account-label.js +6 -6
  124. package/dist/src/tiles/list/date-label.d.ts +1 -1
  125. package/dist/src/tiles/list/date-label.js +12 -12
  126. package/dist/src/tiles/list/tile-list-compact-header.d.ts +12 -12
  127. package/dist/src/tiles/list/tile-list-compact-header.js +41 -41
  128. package/dist/src/tiles/list/tile-list-compact.d.ts +21 -21
  129. package/dist/src/tiles/list/tile-list-compact.js +93 -93
  130. package/dist/src/tiles/list/tile-list.d.ts +50 -50
  131. package/dist/src/tiles/list/tile-list.js +275 -272
  132. package/dist/src/tiles/list/tile-list.js.map +1 -1
  133. package/dist/src/tiles/mediatype-icon.d.ts +9 -9
  134. package/dist/src/tiles/mediatype-icon.js +47 -47
  135. package/dist/src/tiles/overlay/icon-overlay.d.ts +7 -7
  136. package/dist/src/tiles/overlay/icon-overlay.js +30 -30
  137. package/dist/src/tiles/overlay/text-overlay.d.ts +8 -8
  138. package/dist/src/tiles/overlay/text-overlay.js +31 -31
  139. package/dist/src/tiles/text-snippet-block.d.ts +29 -29
  140. package/dist/src/tiles/text-snippet-block.js +81 -81
  141. package/dist/src/tiles/tile-dispatcher.d.ts +36 -36
  142. package/dist/src/tiles/tile-dispatcher.js +128 -128
  143. package/dist/src/utils/analytics-events.d.ts +22 -22
  144. package/dist/src/utils/analytics-events.js +24 -24
  145. package/dist/src/utils/format-count.d.ts +7 -7
  146. package/dist/src/utils/format-count.js +76 -76
  147. package/dist/src/utils/format-date.d.ts +2 -2
  148. package/dist/src/utils/format-date.js +23 -23
  149. package/dist/test/collection-browser.test.d.ts +1 -1
  150. package/dist/test/collection-browser.test.js +583 -444
  151. package/dist/test/collection-browser.test.js.map +1 -1
  152. package/dist/test/collection-facets/facets-template.test.d.ts +1 -1
  153. package/dist/test/collection-facets/facets-template.test.js +62 -62
  154. package/dist/test/collection-facets/more-facets-content.test.d.ts +1 -1
  155. package/dist/test/collection-facets/more-facets-content.test.js +114 -114
  156. package/dist/test/collection-facets/more-facets-pagination.test.d.ts +1 -1
  157. package/dist/test/collection-facets/more-facets-pagination.test.js +117 -117
  158. package/dist/test/collection-facets.test.d.ts +2 -2
  159. package/dist/test/collection-facets.test.js +544 -498
  160. package/dist/test/collection-facets.test.js.map +1 -1
  161. package/dist/test/empty-placeholder.test.d.ts +1 -1
  162. package/dist/test/empty-placeholder.test.js +33 -33
  163. package/dist/test/icon-overlay.test.d.ts +1 -1
  164. package/dist/test/icon-overlay.test.js +24 -24
  165. package/dist/test/image-block.test.d.ts +1 -1
  166. package/dist/test/image-block.test.js +48 -48
  167. package/dist/test/item-image.test.d.ts +1 -1
  168. package/dist/test/item-image.test.js +56 -56
  169. package/dist/test/mediatype-config.test.d.ts +1 -1
  170. package/dist/test/mediatype-config.test.js +16 -16
  171. package/dist/test/mocks/mock-analytics-handler.d.ts +10 -10
  172. package/dist/test/mocks/mock-analytics-handler.js +15 -15
  173. package/dist/test/mocks/mock-collection-name-cache.d.ts +7 -7
  174. package/dist/test/mocks/mock-collection-name-cache.js +13 -13
  175. package/dist/test/mocks/mock-search-responses.d.ts +10 -8
  176. package/dist/test/mocks/mock-search-responses.js +271 -198
  177. package/dist/test/mocks/mock-search-responses.js.map +1 -1
  178. package/dist/test/mocks/mock-search-service.d.ts +13 -13
  179. package/dist/test/mocks/mock-search-service.js +36 -32
  180. package/dist/test/mocks/mock-search-service.js.map +1 -1
  181. package/dist/test/restoration-state-handler.test.d.ts +1 -1
  182. package/dist/test/restoration-state-handler.test.js +126 -126
  183. package/dist/test/sort-filter-bar/sort-filter-bar.test.d.ts +1 -1
  184. package/dist/test/sort-filter-bar/sort-filter-bar.test.js +113 -113
  185. package/dist/test/text-overlay.test.d.ts +1 -1
  186. package/dist/test/text-overlay.test.js +41 -41
  187. package/dist/test/text-snippet-block.test.d.ts +1 -1
  188. package/dist/test/text-snippet-block.test.js +57 -57
  189. package/dist/test/tile-stats.test.d.ts +1 -1
  190. package/dist/test/tile-stats.test.js +33 -33
  191. package/dist/test/tiles/grid/item-tile.test.d.ts +1 -1
  192. package/dist/test/tiles/grid/item-tile.test.js +107 -107
  193. package/dist/test/tiles/list/tile-list-compact.test.d.ts +1 -1
  194. package/dist/test/tiles/list/tile-list-compact.test.js +92 -92
  195. package/dist/test/tiles/list/tile-list.test.d.ts +1 -1
  196. package/dist/test/tiles/list/tile-list.test.js +96 -67
  197. package/dist/test/tiles/list/tile-list.test.js.map +1 -1
  198. package/dist/test/utils/format-count.test.d.ts +1 -1
  199. package/dist/test/utils/format-count.test.js +23 -23
  200. package/dist/test/utils/format-date.test.d.ts +1 -1
  201. package/dist/test/utils/format-date.test.js +17 -17
  202. package/index.html +24 -24
  203. package/local.archive.org.cert +86 -86
  204. package/local.archive.org.key +27 -27
  205. package/package.json +3 -3
  206. package/renovate.json +6 -6
  207. package/src/app-root.ts +32 -9
  208. package/src/collection-browser.ts +130 -35
  209. package/src/collection-facets.ts +2 -3
  210. package/src/tiles/list/tile-list.ts +6 -2
  211. package/test/collection-browser.test.ts +207 -0
  212. package/test/collection-facets.test.ts +67 -0
  213. package/test/mocks/mock-search-responses.ts +82 -0
  214. package/test/mocks/mock-search-service.ts +6 -0
  215. package/test/tiles/list/tile-list.test.ts +40 -0
  216. package/tsconfig.json +21 -21
  217. package/web-dev-server.config.mjs +30 -30
  218. package/web-test-runner.config.mjs +41 -41
@@ -4,6 +4,7 @@ import { html } from 'lit';
4
4
  import sinon from 'sinon';
5
5
  import type { InfiniteScroller } from '@internetarchive/infinite-scroller';
6
6
  import { SearchType } from '@internetarchive/search-service';
7
+ import type { HistogramDateRange } from '@internetarchive/histogram-date-range';
7
8
  import type { CollectionBrowser } from '../src/collection-browser';
8
9
  import '../src/collection-browser';
9
10
  import {
@@ -17,6 +18,7 @@ import { MockCollectionNameCache } from './mocks/mock-collection-name-cache';
17
18
  import { MockAnalyticsHandler } from './mocks/mock-analytics-handler';
18
19
  import { analyticsCategories } from '../src/utils/analytics-events';
19
20
  import type { TileDispatcher } from '../src/tiles/tile-dispatcher';
21
+ import type { CollectionFacets } from '../src/collection-facets';
20
22
 
21
23
  describe('Collection Browser', () => {
22
24
  beforeEach(async () => {
@@ -289,6 +291,98 @@ describe('Collection Browser', () => {
289
291
  );
290
292
  });
291
293
 
294
+ it('fires a separate date histogram query when year facets are applied', async () => {
295
+ const searchService = new MockSearchService();
296
+ const selectedFacets: SelectedFacets = {
297
+ subject: {},
298
+ lending: {},
299
+ mediatype: {},
300
+ language: {},
301
+ creator: {},
302
+ collection: {},
303
+ year: {
304
+ '2000': {
305
+ key: '2000',
306
+ state: 'selected',
307
+ count: 123,
308
+ },
309
+ },
310
+ };
311
+
312
+ const el = await fixture<CollectionBrowser>(
313
+ html`<collection-browser .searchService=${searchService}>
314
+ </collection-browser>`
315
+ );
316
+
317
+ el.baseQuery = 'collection:foo';
318
+ el.showHistogramDatePicker = true;
319
+ el.selectedFacets = selectedFacets;
320
+ await el.updateComplete;
321
+
322
+ expect(
323
+ searchService.searchParams?.aggregations?.simpleParams
324
+ ).to.deep.equal(['year']); // Explicitly requests year aggregations
325
+ expect(searchService.searchParams?.query).to.equal(
326
+ 'collection:foo' // No date clause on query
327
+ );
328
+ });
329
+
330
+ it('fires a separate date histogram query when a date range is applied', async () => {
331
+ const searchService = new MockSearchService();
332
+
333
+ const el = await fixture<CollectionBrowser>(
334
+ html`<collection-browser .searchService=${searchService}>
335
+ </collection-browser>`
336
+ );
337
+
338
+ el.baseQuery = 'collection:foo';
339
+ el.showHistogramDatePicker = true;
340
+ el.dateRangeQueryClause = 'year:[1995 TO 2005]';
341
+ await el.updateComplete;
342
+
343
+ expect(
344
+ searchService.searchParams?.aggregations?.simpleParams
345
+ ).to.deep.equal(['year']); // Explicitly requests year aggregations
346
+ expect(searchService.searchParams?.query).to.equal(
347
+ 'collection:foo' // No date clause on query
348
+ );
349
+ });
350
+
351
+ it('does not fire a separate date histogram query when no date filters are applied', async () => {
352
+ const searchService = new MockSearchService();
353
+
354
+ const el = await fixture<CollectionBrowser>(
355
+ html`<collection-browser .searchService=${searchService}>
356
+ </collection-browser>`
357
+ );
358
+
359
+ el.baseQuery = 'collection:foo';
360
+ el.showHistogramDatePicker = true;
361
+ await el.updateComplete;
362
+
363
+ expect(searchService.searchParams?.aggregations?.simpleParams).to.satisfy(
364
+ (aggTypes: string[]) => !aggTypes || !aggTypes.includes('year') // Not requesting year aggregations explicitly
365
+ );
366
+ });
367
+
368
+ it('does not fire a separate date histogram query when date picker is disabled', async () => {
369
+ const searchService = new MockSearchService();
370
+
371
+ const el = await fixture<CollectionBrowser>(
372
+ html`<collection-browser .searchService=${searchService}>
373
+ </collection-browser>`
374
+ );
375
+
376
+ el.baseQuery = 'collection:foo';
377
+ el.showHistogramDatePicker = false;
378
+ el.dateRangeQueryClause = 'year:[1995 TO 2005]';
379
+ await el.updateComplete;
380
+
381
+ expect(searchService.searchParams?.aggregations?.simpleParams).to.satisfy(
382
+ (aggTypes: string[]) => !aggTypes || !aggTypes.includes('year') // Not requesting year aggregations explicitly
383
+ );
384
+ });
385
+
292
386
  it('fails gracefully if no search service provided', async () => {
293
387
  const el = await fixture<CollectionBrowser>(
294
388
  html`<collection-browser></collection-browser>`
@@ -381,6 +475,28 @@ describe('Collection Browser', () => {
381
475
  expect(cell.model?.contentWarning).to.be.true;
382
476
  });
383
477
 
478
+ it('joins full description array into a single string with line breaks', async () => {
479
+ const searchService = new MockSearchService();
480
+
481
+ const el = await fixture<CollectionBrowser>(
482
+ html`<collection-browser .searchService=${searchService}>
483
+ </collection-browser>`
484
+ );
485
+
486
+ // This query receives an array description like ['line1', 'line2']
487
+ el.baseQuery = 'multi-line-description';
488
+ await el.updateComplete;
489
+
490
+ const cellTemplate = el.cellForIndex(0);
491
+ expect(cellTemplate).to.exist;
492
+
493
+ const cell = await fixture<TileDispatcher>(cellTemplate!);
494
+ expect(cell).to.exist;
495
+
496
+ // Actual model description should be joined
497
+ expect(cell.model?.description).to.equal('line1\nline2');
498
+ });
499
+
384
500
  it('can search on demand if only search type has changed', async () => {
385
501
  const searchService = new MockSearchService();
386
502
 
@@ -552,6 +668,97 @@ describe('Collection Browser', () => {
552
668
  expect(el.selectedSort).to.equal(SortField.title);
553
669
  });
554
670
 
671
+ it('sets sort filter properties when user selects title filter', async () => {
672
+ const searchService = new MockSearchService();
673
+ const el = await fixture<CollectionBrowser>(
674
+ html`<collection-browser .searchService=${searchService}>
675
+ </collection-browser>`
676
+ );
677
+
678
+ el.baseQuery = 'collection:foo';
679
+ el.selectedTitleFilter = 'X';
680
+ await el.updateComplete;
681
+
682
+ // Wait an extra tick
683
+ await new Promise(res => {
684
+ setTimeout(res, 0);
685
+ });
686
+
687
+ expect(searchService.searchParams?.query).to.equal(
688
+ 'collection:foo AND firstTitle:X'
689
+ );
690
+ });
691
+
692
+ it('sets sort filter properties when user selects creator filter', async () => {
693
+ const searchService = new MockSearchService();
694
+ const el = await fixture<CollectionBrowser>(
695
+ html`<collection-browser .searchService=${searchService}>
696
+ </collection-browser>`
697
+ );
698
+
699
+ el.baseQuery = 'collection:foo';
700
+ el.selectedCreatorFilter = 'X';
701
+ await el.updateComplete;
702
+
703
+ // Wait an extra tick
704
+ await new Promise(res => {
705
+ setTimeout(res, 0);
706
+ });
707
+
708
+ expect(searchService.searchParams?.query).to.equal(
709
+ 'collection:foo AND firstCreator:X'
710
+ );
711
+ });
712
+
713
+ it('sets date range query when date picker selection changed', async () => {
714
+ const searchService = new MockSearchService();
715
+ const el = await fixture<CollectionBrowser>(
716
+ html`<collection-browser .searchService=${searchService}>
717
+ </collection-browser>`
718
+ );
719
+
720
+ el.baseQuery = 'years'; // Includes year_histogram aggregation in response
721
+ el.showHistogramDatePicker = true;
722
+ await el.updateComplete;
723
+
724
+ const facets = el.shadowRoot?.querySelector(
725
+ 'collection-facets'
726
+ ) as CollectionFacets;
727
+ await facets?.updateComplete;
728
+
729
+ // Wait for the date picker to be rendered (which may take until the next tick)
730
+ await new Promise(res => {
731
+ setTimeout(res, 0);
732
+ });
733
+
734
+ const histogram = facets?.shadowRoot?.querySelector(
735
+ 'histogram-date-range'
736
+ ) as HistogramDateRange;
737
+ expect(histogram).to.exist;
738
+
739
+ // Enter a new min date into the date picker
740
+ const minDateInput = histogram.shadowRoot?.querySelector(
741
+ '#date-min'
742
+ ) as HTMLInputElement;
743
+
744
+ const pressEnterEvent = new KeyboardEvent('keyup', {
745
+ key: 'Enter',
746
+ });
747
+
748
+ minDateInput.value = '1960';
749
+ minDateInput.dispatchEvent(pressEnterEvent);
750
+
751
+ // Wait for the histogram's update delay
752
+ await new Promise(res => {
753
+ setTimeout(res, histogram.updateDelay + 50);
754
+ });
755
+
756
+ // Ensure that the histogram change propagated to the collection browser's
757
+ // date query correctly.
758
+ await el.updateComplete;
759
+ expect(el.dateRangeQueryClause).to.equal('year:[1960 TO 2000]');
760
+ });
761
+
555
762
  it('scrolls to page', async () => {
556
763
  const searchService = new MockSearchService();
557
764
  const el = await fixture<CollectionBrowser>(
@@ -34,6 +34,73 @@ describe('Collection Facets', () => {
34
34
  el.shadowRoot?.querySelector('#container')?.classList.contains('loading')
35
35
  ).to.be.false;
36
36
  });
37
+
38
+ it('renders a date picker loading placeholder when date picker enabled', async () => {
39
+ const el = await fixture<CollectionFacets>(
40
+ html`<collection-facets></collection-facets>`
41
+ );
42
+
43
+ el.fullYearAggregationLoading = true;
44
+ el.showHistogramDatePicker = true;
45
+ await el.updateComplete;
46
+
47
+ const histogramLoader = el.shadowRoot?.querySelector(
48
+ '.histogram-loading-indicator'
49
+ );
50
+ expect(histogramLoader).to.exist;
51
+ });
52
+
53
+ it('does not render a date picker loading placeholder when date picker disabled', async () => {
54
+ const el = await fixture<CollectionFacets>(
55
+ html`<collection-facets></collection-facets>`
56
+ );
57
+
58
+ el.fullYearAggregationLoading = true;
59
+ el.showHistogramDatePicker = false;
60
+ await el.updateComplete;
61
+
62
+ const histogramLoader = el.shadowRoot?.querySelector(
63
+ '.histogram-loading-indicator'
64
+ );
65
+ expect(histogramLoader).to.be.null;
66
+ });
67
+
68
+ it('renders the date picker when enabled with data present', async () => {
69
+ const el = await fixture<CollectionFacets>(
70
+ html`<collection-facets></collection-facets>`
71
+ );
72
+
73
+ el.fullYearAggregationLoading = false;
74
+ el.showHistogramDatePicker = true;
75
+ el.fullYearsHistogramAggregation = new Aggregation({
76
+ buckets: [1, 2, 3],
77
+ first_bucket_key: 0,
78
+ last_bucket_key: 2,
79
+ });
80
+ await el.updateComplete;
81
+
82
+ const histogram = el.shadowRoot?.querySelector('histogram-date-range');
83
+ expect(histogram).to.exist;
84
+ });
85
+
86
+ it('does not render the date picker when disabled', async () => {
87
+ const el = await fixture<CollectionFacets>(
88
+ html`<collection-facets></collection-facets>`
89
+ );
90
+
91
+ el.fullYearAggregationLoading = false;
92
+ el.showHistogramDatePicker = false;
93
+ el.fullYearsHistogramAggregation = new Aggregation({
94
+ buckets: [1, 2, 3],
95
+ first_bucket_key: 0,
96
+ last_bucket_key: 2,
97
+ });
98
+ await el.updateComplete;
99
+
100
+ const histogram = el.shadowRoot?.querySelector('histogram-date-range');
101
+ expect(histogram).to.be.null;
102
+ });
103
+
37
104
  it('renders aggregations as facets', async () => {
38
105
  const el = await fixture<CollectionFacets>(
39
106
  html`<collection-facets></collection-facets>`
@@ -1,5 +1,6 @@
1
1
  import type { Result } from '@internetarchive/result-type';
2
2
  import {
3
+ Aggregation,
3
4
  ItemHit,
4
5
  SearchResponse,
5
6
  SearchServiceError,
@@ -40,6 +41,51 @@ export const mockSuccessSingleResult: Result<
40
41
  },
41
42
  };
42
43
 
44
+ export const mockSuccessWithYearHistogramAggs: Result<
45
+ SearchResponse,
46
+ SearchServiceError
47
+ > = {
48
+ success: {
49
+ request: {
50
+ clientParameters: {
51
+ user_query: 'years',
52
+ sort: [],
53
+ },
54
+ finalizedParameters: {
55
+ user_query: 'years',
56
+ sort: [],
57
+ },
58
+ },
59
+ rawResponse: {},
60
+ response: {
61
+ totalResults: 1,
62
+ returnedCount: 1,
63
+ aggregations: {
64
+ subject: new Aggregation({
65
+ buckets: [
66
+ {
67
+ key: 'foo',
68
+ doc_count: 3,
69
+ },
70
+ ],
71
+ }),
72
+ year_histogram: new Aggregation({
73
+ buckets: [1, 2, 3, 3, 2, 1],
74
+ first_bucket_key: 1950,
75
+ last_bucket_key: 2000,
76
+ interval: 10,
77
+ number_buckets: 6,
78
+ }),
79
+ },
80
+ results: [],
81
+ },
82
+ responseHeader: {
83
+ succeeded: true,
84
+ query_time: 0,
85
+ },
86
+ },
87
+ };
88
+
43
89
  export const mockSuccessLoggedInResult: Result<
44
90
  SearchResponse,
45
91
  SearchServiceError
@@ -224,3 +270,39 @@ export const mockSuccessMultipleResults: Result<
224
270
  },
225
271
  },
226
272
  };
273
+
274
+ export const mockSuccessMultiLineDescription: Result<
275
+ SearchResponse,
276
+ SearchServiceError
277
+ > = {
278
+ success: {
279
+ request: {
280
+ clientParameters: {
281
+ user_query: 'multi-line-description',
282
+ sort: [],
283
+ },
284
+ finalizedParameters: {
285
+ user_query: 'multi-line-description',
286
+ sort: [],
287
+ },
288
+ },
289
+ rawResponse: {},
290
+ response: {
291
+ totalResults: 1,
292
+ returnedCount: 1,
293
+ results: [
294
+ new ItemHit({
295
+ fields: {
296
+ identifier: 'foo',
297
+ collection: ['foo', 'bar'],
298
+ description: ['line1', 'line2'],
299
+ },
300
+ }),
301
+ ],
302
+ },
303
+ responseHeader: {
304
+ succeeded: true,
305
+ query_time: 0,
306
+ },
307
+ },
308
+ };
@@ -13,6 +13,8 @@ import {
13
13
  mockSuccessLoggedInResult,
14
14
  mockSuccessNoPreviewResult,
15
15
  mockSuccessLoggedInAndNoPreviewResult,
16
+ mockSuccessWithYearHistogramAggs,
17
+ mockSuccessMultiLineDescription,
16
18
  } from './mock-search-responses';
17
19
 
18
20
  export class MockSearchService implements SearchServiceInterface {
@@ -46,6 +48,10 @@ export class MockSearchService implements SearchServiceInterface {
46
48
  switch (this.searchParams?.query) {
47
49
  case 'single-result':
48
50
  return mockSuccessSingleResult;
51
+ case 'years':
52
+ return mockSuccessWithYearHistogramAggs;
53
+ case 'multi-line-description':
54
+ return mockSuccessMultiLineDescription;
49
55
  case 'loggedin':
50
56
  return mockSuccessLoggedInResult;
51
57
  case 'no-preview':
@@ -105,4 +105,44 @@ describe('List Tile', () => {
105
105
  expect(dateRow).to.exist;
106
106
  expect(dateRow?.textContent?.trim()).to.contain('Added: Jan 01, 2010');
107
107
  });
108
+
109
+ it('should render links to /search pages (not search.php) for subject, creator, and source', async () => {
110
+ const model: Partial<TileModel> = {
111
+ subjects: ['foo'],
112
+ creators: ['bar'],
113
+ source: 'baz',
114
+ };
115
+
116
+ const el = await fixture<TileList>(html`
117
+ <tile-list .model=${model}></tile-list>
118
+ `);
119
+
120
+ const subjectLink = el.shadowRoot?.querySelector('#topics a[href]');
121
+ expect(subjectLink).to.exist;
122
+ expect(subjectLink?.getAttribute('href')).to.equal(
123
+ `/search?query=${encodeURIComponent('subject:"foo"')}`
124
+ );
125
+
126
+ const creatorLink = el.shadowRoot?.querySelector('#creator a[href]');
127
+ expect(creatorLink).to.exist;
128
+ expect(creatorLink?.getAttribute('href')).to.equal(
129
+ `/search?query=${encodeURIComponent('creator:"bar"')}`
130
+ );
131
+
132
+ const sourceLink = el.shadowRoot?.querySelector('#source a[href]');
133
+ expect(sourceLink).to.exist;
134
+ expect(sourceLink?.getAttribute('href')).to.equal(
135
+ `/search?query=${encodeURIComponent('source:"baz"')}`
136
+ );
137
+ });
138
+
139
+ it('should render multi-line descriptions with spaces b/w lines', async () => {
140
+ const el = await fixture<TileList>(html`
141
+ <tile-list .model=${{ description: 'line1\nline2' }}> </tile-list>
142
+ `);
143
+
144
+ const descriptionBlock = el.shadowRoot?.getElementById('description');
145
+ expect(descriptionBlock).to.exist;
146
+ expect(descriptionBlock?.textContent?.trim()).to.equal('line1 line2'); // line break replaced by space
147
+ });
108
148
  });
package/tsconfig.json CHANGED
@@ -1,21 +1,21 @@
1
- {
2
- "compilerOptions": {
3
- "target": "es2018",
4
- "module": "esnext",
5
- "moduleResolution": "node",
6
- "noEmitOnError": true,
7
- "lib": ["es2017", "dom"],
8
- "strict": true,
9
- "esModuleInterop": false,
10
- "allowSyntheticDefaultImports": true,
11
- "experimentalDecorators": true,
12
- "importHelpers": true,
13
- "outDir": "dist",
14
- "sourceMap": true,
15
- "inlineSources": true,
16
- "rootDir": "./",
17
- "declaration": true,
18
- "importsNotUsedAsValues": "error"
19
- },
20
- "include": ["src", "test", "index.ts", "types"],
21
- }
1
+ {
2
+ "compilerOptions": {
3
+ "target": "es2018",
4
+ "module": "esnext",
5
+ "moduleResolution": "node",
6
+ "noEmitOnError": true,
7
+ "lib": ["es2017", "dom"],
8
+ "strict": true,
9
+ "esModuleInterop": false,
10
+ "allowSyntheticDefaultImports": true,
11
+ "experimentalDecorators": true,
12
+ "importHelpers": true,
13
+ "outDir": "dist",
14
+ "sourceMap": true,
15
+ "inlineSources": true,
16
+ "rootDir": "./",
17
+ "declaration": true,
18
+ "importsNotUsedAsValues": "error"
19
+ },
20
+ "include": ["src", "test", "index.ts", "types"],
21
+ }
@@ -1,30 +1,30 @@
1
- // import { hmrPlugin, presets } from '@open-wc/dev-server-hmr';
2
-
3
- /** Use Hot Module replacement by adding --hmr to the start command */
4
- const hmr = process.argv.includes('--hmr');
5
-
6
- export default /** @type {import('@web/dev-server').DevServerConfig} */ ({
7
- nodeResolve: true,
8
- open: '/',
9
- watch: !hmr,
10
-
11
- /** Compile JS for older browsers. Requires @web/dev-server-esbuild plugin */
12
- // esbuildTarget: 'auto'
13
-
14
- /** Set appIndex to enable SPA routing */
15
- // appIndex: 'demo/index.html',
16
-
17
- /** Confgure bare import resolve plugin */
18
- // nodeResolve: {
19
- // exportConditions: ['browser', 'development']
20
- // },
21
-
22
- plugins: [
23
- /** Use Hot Module Replacement by uncommenting. Requires @open-wc/dev-server-hmr plugin */
24
- // hmr && hmrPlugin({ exclude: ['**/*/node_modules/**/*'], presets: [presets.litElement] }),
25
- ],
26
-
27
- http2: true,
28
- sslCert: './local.archive.org.cert',
29
- sslKey: './local.archive.org.key',
30
- });
1
+ // import { hmrPlugin, presets } from '@open-wc/dev-server-hmr';
2
+
3
+ /** Use Hot Module replacement by adding --hmr to the start command */
4
+ const hmr = process.argv.includes('--hmr');
5
+
6
+ export default /** @type {import('@web/dev-server').DevServerConfig} */ ({
7
+ nodeResolve: true,
8
+ open: '/',
9
+ watch: !hmr,
10
+
11
+ /** Compile JS for older browsers. Requires @web/dev-server-esbuild plugin */
12
+ // esbuildTarget: 'auto'
13
+
14
+ /** Set appIndex to enable SPA routing */
15
+ // appIndex: 'demo/index.html',
16
+
17
+ /** Confgure bare import resolve plugin */
18
+ // nodeResolve: {
19
+ // exportConditions: ['browser', 'development']
20
+ // },
21
+
22
+ plugins: [
23
+ /** Use Hot Module Replacement by uncommenting. Requires @open-wc/dev-server-hmr plugin */
24
+ // hmr && hmrPlugin({ exclude: ['**/*/node_modules/**/*'], presets: [presets.litElement] }),
25
+ ],
26
+
27
+ http2: true,
28
+ sslCert: './local.archive.org.cert',
29
+ sslKey: './local.archive.org.key',
30
+ });
@@ -1,41 +1,41 @@
1
- // import { playwrightLauncher } from '@web/test-runner-playwright';
2
-
3
- const filteredLogs = ['Running in dev mode', 'lit-html is in dev mode'];
4
-
5
- export default /** @type {import("@web/test-runner").TestRunnerConfig} */ ({
6
- /** Test files to run */
7
- files: 'dist/test/**/*.test.js',
8
-
9
- /** Resolve bare module imports */
10
- nodeResolve: {
11
- exportConditions: ['browser', 'development'],
12
- },
13
-
14
- /** Filter out lit dev mode logs */
15
- filterBrowserLogs(log) {
16
- for (const arg of log.args) {
17
- if (typeof arg === 'string' && filteredLogs.some(l => arg.includes(l))) {
18
- return false;
19
- }
20
- }
21
- return true;
22
- },
23
-
24
- /** Compile JS for older browsers. Requires @web/dev-server-esbuild plugin */
25
- // esbuildTarget: 'auto',
26
-
27
- /** Amount of browsers to run concurrently */
28
- // concurrentBrowsers: 2,
29
-
30
- /** Amount of test files per browser to test concurrently */
31
- // concurrency: 1,
32
-
33
- /** Browsers to run tests on */
34
- // browsers: [
35
- // playwrightLauncher({ product: 'chromium' }),
36
- // playwrightLauncher({ product: 'firefox' }),
37
- // playwrightLauncher({ product: 'webkit' }),
38
- // ],
39
-
40
- // See documentation for all available options
41
- });
1
+ // import { playwrightLauncher } from '@web/test-runner-playwright';
2
+
3
+ const filteredLogs = ['Running in dev mode', 'lit-html is in dev mode'];
4
+
5
+ export default /** @type {import("@web/test-runner").TestRunnerConfig} */ ({
6
+ /** Test files to run */
7
+ files: 'dist/test/**/*.test.js',
8
+
9
+ /** Resolve bare module imports */
10
+ nodeResolve: {
11
+ exportConditions: ['browser', 'development'],
12
+ },
13
+
14
+ /** Filter out lit dev mode logs */
15
+ filterBrowserLogs(log) {
16
+ for (const arg of log.args) {
17
+ if (typeof arg === 'string' && filteredLogs.some(l => arg.includes(l))) {
18
+ return false;
19
+ }
20
+ }
21
+ return true;
22
+ },
23
+
24
+ /** Compile JS for older browsers. Requires @web/dev-server-esbuild plugin */
25
+ // esbuildTarget: 'auto',
26
+
27
+ /** Amount of browsers to run concurrently */
28
+ // concurrentBrowsers: 2,
29
+
30
+ /** Amount of test files per browser to test concurrently */
31
+ // concurrency: 1,
32
+
33
+ /** Browsers to run tests on */
34
+ // browsers: [
35
+ // playwrightLauncher({ product: 'chromium' }),
36
+ // playwrightLauncher({ product: 'firefox' }),
37
+ // playwrightLauncher({ product: 'webkit' }),
38
+ // ],
39
+
40
+ // See documentation for all available options
41
+ });