@internetarchive/collection-browser 4.2.0-alpha-webdev8164.3 → 4.2.1-alpha-webdev7004.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 (88) hide show
  1. package/.claude/settings.local.json +11 -0
  2. package/.editorconfig +29 -29
  3. package/.github/workflows/ci.yml +27 -27
  4. package/.github/workflows/gh-pages-main.yml +39 -39
  5. package/.github/workflows/npm-publish.yml +39 -39
  6. package/.github/workflows/pr-preview.yml +38 -38
  7. package/.husky/pre-commit +1 -1
  8. package/.prettierignore +1 -1
  9. package/LICENSE +661 -661
  10. package/README.md +83 -83
  11. package/dist/src/app-root.js +4 -0
  12. package/dist/src/app-root.js.map +1 -1
  13. package/dist/src/collection-browser.js +32 -27
  14. package/dist/src/collection-browser.js.map +1 -1
  15. package/dist/src/collection-facets/facets-template.js +0 -5
  16. package/dist/src/collection-facets/facets-template.js.map +1 -1
  17. package/dist/src/collection-facets/more-facets-content.d.ts +8 -106
  18. package/dist/src/collection-facets/more-facets-content.js +103 -612
  19. package/dist/src/collection-facets/more-facets-content.js.map +1 -1
  20. package/dist/src/collection-facets/more-facets-pagination.d.ts +3 -12
  21. package/dist/src/collection-facets/more-facets-pagination.js +9 -71
  22. package/dist/src/collection-facets/more-facets-pagination.js.map +1 -1
  23. package/dist/src/collection-facets/toggle-switch.js +0 -1
  24. package/dist/src/collection-facets/toggle-switch.js.map +1 -1
  25. package/dist/src/collection-facets.js +9 -10
  26. package/dist/src/collection-facets.js.map +1 -1
  27. package/dist/src/data-source/collection-browser-data-source.d.ts +7 -0
  28. package/dist/src/data-source/collection-browser-data-source.js +27 -10
  29. package/dist/src/data-source/collection-browser-data-source.js.map +1 -1
  30. package/dist/src/mediatype/mediatype-config.js +1 -1
  31. package/dist/src/mediatype/mediatype-config.js.map +1 -1
  32. package/dist/src/models.d.ts +12 -2
  33. package/dist/src/models.js +13 -8
  34. package/dist/src/models.js.map +1 -1
  35. package/dist/src/restoration-state-handler.js +9 -3
  36. package/dist/src/restoration-state-handler.js.map +1 -1
  37. package/dist/src/tiles/hover/hover-pane-controller.js +2 -1
  38. package/dist/src/tiles/hover/hover-pane-controller.js.map +1 -1
  39. package/dist/src/tiles/tile-dispatcher.d.ts +6 -0
  40. package/dist/src/tiles/tile-dispatcher.js +11 -3
  41. package/dist/src/tiles/tile-dispatcher.js.map +1 -1
  42. package/dist/test/collection-browser.test.js +72 -0
  43. package/dist/test/collection-browser.test.js.map +1 -1
  44. package/dist/test/collection-facets/more-facets-content.test.js +3 -212
  45. package/dist/test/collection-facets/more-facets-content.test.js.map +1 -1
  46. package/dist/test/collection-facets/more-facets-pagination.test.js +3 -63
  47. package/dist/test/collection-facets/more-facets-pagination.test.js.map +1 -1
  48. package/dist/test/data-source/collection-browser-data-source.test.js +52 -0
  49. package/dist/test/data-source/collection-browser-data-source.test.js.map +1 -1
  50. package/dist/test/mocks/mock-search-responses.d.ts +0 -5
  51. package/dist/test/mocks/mock-search-responses.js +0 -44
  52. package/dist/test/mocks/mock-search-responses.js.map +1 -1
  53. package/dist/test/mocks/mock-search-service.js +1 -2
  54. package/dist/test/mocks/mock-search-service.js.map +1 -1
  55. package/dist/test/tiles/tile-dispatcher.test.js +14 -0
  56. package/dist/test/tiles/tile-dispatcher.test.js.map +1 -1
  57. package/dist/test/tiles/tile-mediatype-icon.test.js +4 -4
  58. package/dist/test/tiles/tile-mediatype-icon.test.js.map +1 -1
  59. package/eslint.config.mjs +53 -53
  60. package/index.html +24 -24
  61. package/local.archive.org.cert +86 -86
  62. package/local.archive.org.key +27 -27
  63. package/package.json +120 -121
  64. package/renovate.json +6 -6
  65. package/src/app-root.ts +4 -0
  66. package/src/collection-browser.ts +43 -36
  67. package/src/collection-facets/facets-template.ts +0 -5
  68. package/src/collection-facets/more-facets-content.ts +113 -662
  69. package/src/collection-facets/more-facets-pagination.ts +10 -84
  70. package/src/collection-facets/toggle-switch.ts +0 -1
  71. package/src/collection-facets.ts +13 -10
  72. package/src/data-source/collection-browser-data-source.ts +31 -10
  73. package/src/mediatype/mediatype-config.ts +1 -1
  74. package/src/models.ts +30 -8
  75. package/src/restoration-state-handler.ts +7 -3
  76. package/src/tiles/hover/hover-pane-controller.ts +2 -1
  77. package/src/tiles/tile-dispatcher.ts +12 -3
  78. package/test/collection-browser.test.ts +105 -0
  79. package/test/collection-facets/more-facets-content.test.ts +4 -326
  80. package/test/collection-facets/more-facets-pagination.test.ts +3 -87
  81. package/test/data-source/collection-browser-data-source.test.ts +62 -0
  82. package/test/mocks/mock-search-responses.ts +0 -48
  83. package/test/mocks/mock-search-service.ts +0 -2
  84. package/test/tiles/tile-dispatcher.test.ts +17 -0
  85. package/test/tiles/tile-mediatype-icon.test.ts +4 -4
  86. package/tsconfig.json +25 -25
  87. package/web-dev-server.config.mjs +30 -30
  88. package/web-test-runner.config.mjs +52 -52
@@ -5,10 +5,7 @@ import '../../src/collection-facets/more-facets-content';
5
5
  import { MockSearchService } from '../mocks/mock-search-service';
6
6
  import { MockAnalyticsHandler } from '../mocks/mock-analytics-handler';
7
7
  import type { FacetsTemplate } from '../../src/collection-facets/facets-template';
8
- import {
9
- getDefaultSelectedFacets,
10
- type SelectedFacets,
11
- } from '../../src/models';
8
+ import type { SelectedFacets } from '../../src/models';
12
9
 
13
10
  const selectedFacetsGroup = {
14
11
  title: 'Media Type',
@@ -57,7 +54,7 @@ describe('More facets content', () => {
57
54
  expect(el.shadowRoot?.querySelector('.facets-loader')).to.exist;
58
55
  });
59
56
 
60
- it('should NOT render pagination when facet count < 1000', async () => {
57
+ it('should render pagination for more facets', async () => {
61
58
  const searchService = new MockSearchService();
62
59
 
63
60
  const el = await fixture<MoreFacetsContent>(
@@ -67,22 +64,11 @@ describe('More facets content', () => {
67
64
  );
68
65
 
69
66
  el.facetKey = 'year';
70
- el.query = 'more-facets'; // Produces a response with 45 aggregations (< 1000)
67
+ el.query = 'more-facets'; // Produces a response with 40+ aggregations for multiple pages
71
68
  await el.updateComplete;
72
69
  await aTimeout(50); // Give it a moment to perform the (mock) search query after the initial update
73
70
 
74
- // Verify pagination component is NOT present (horizontal scroll mode)
75
- expect(el.shadowRoot?.querySelector('more-facets-pagination')).to.not.exist;
76
-
77
- // Verify horizontal scroll mode CSS class is applied
78
- expect(
79
- el.shadowRoot?.querySelector('.facets-content.horizontal-scroll-mode'),
80
- ).to.exist;
81
-
82
- // Verify footer still exists with buttons
83
- expect(el.shadowRoot?.querySelector('.footer')).to.exist;
84
- expect(el.shadowRoot?.querySelector('.btn-cancel')).to.exist;
85
- expect(el.shadowRoot?.querySelector('.btn-submit')).to.exist;
71
+ expect(el.shadowRoot?.querySelectorAll('more-facets-pagination')).to.exist;
86
72
  });
87
73
 
88
74
  it('query for more facets content using search service', async () => {
@@ -242,312 +228,4 @@ describe('More facets content', () => {
242
228
  expect(mockAnalyticsHandler.callAction).to.equal('applyMoreFacetsModal');
243
229
  expect(mockAnalyticsHandler.callLabel).to.equal('collection');
244
230
  });
245
-
246
- it('should have horizontal scrolling enabled', async () => {
247
- const searchService = new MockSearchService();
248
-
249
- const el = await fixture<MoreFacetsContent>(
250
- html`<more-facets-content
251
- .searchService=${searchService}
252
- ></more-facets-content>`,
253
- );
254
-
255
- el.facetKey = 'year';
256
- el.query = 'more-facets';
257
- await el.updateComplete;
258
- await aTimeout(50);
259
-
260
- const facetsContent = el.shadowRoot?.querySelector(
261
- '.facets-content',
262
- ) as HTMLElement;
263
- const styles = window.getComputedStyle(facetsContent);
264
-
265
- expect(styles.overflowX).to.equal('auto');
266
- expect(styles.overflowY).to.equal('hidden');
267
- });
268
-
269
- it('should have horizontal container wrapper', async () => {
270
- const searchService = new MockSearchService();
271
-
272
- const el = await fixture<MoreFacetsContent>(
273
- html`<more-facets-content
274
- .searchService=${searchService}
275
- ></more-facets-content>`,
276
- );
277
-
278
- el.facetKey = 'year';
279
- el.query = 'more-facets';
280
- await el.updateComplete;
281
- await aTimeout(50);
282
-
283
- const container = el.shadowRoot?.querySelector(
284
- '.facets-horizontal-container',
285
- );
286
- expect(container).to.exist;
287
-
288
- const facetsTemplate = container?.querySelector('facets-template');
289
- expect(facetsTemplate).to.exist;
290
- });
291
-
292
- it('should render pagination when facet count >= 1000', async () => {
293
- const searchService = new MockSearchService();
294
-
295
- const el = await fixture<MoreFacetsContent>(
296
- html`<more-facets-content
297
- .searchService=${searchService}
298
- .selectedFacets=${getDefaultSelectedFacets()}
299
- ></more-facets-content>`,
300
- );
301
-
302
- el.facetKey = 'subject';
303
- el.query = 'large-facets'; // Produces a response with 1100 aggregations (>= 1000)
304
- await el.updateComplete;
305
- await aTimeout(50);
306
-
307
- // Verify pagination component IS present
308
- expect(el.shadowRoot?.querySelector('more-facets-pagination')).to.exist;
309
-
310
- // Verify pagination mode CSS class is applied
311
- expect(el.shadowRoot?.querySelector('.facets-content.pagination-mode')).to
312
- .exist;
313
-
314
- // Verify horizontal container wrapper does NOT exist in pagination mode
315
- expect(el.shadowRoot?.querySelector('.facets-horizontal-container')).to.not
316
- .exist;
317
- });
318
-
319
- it('pagination page change should send analytics event', async () => {
320
- const searchService = new MockSearchService();
321
- const mockAnalyticsHandler = new MockAnalyticsHandler();
322
-
323
- const el = await fixture<MoreFacetsContent>(
324
- html`<more-facets-content
325
- .searchService=${searchService}
326
- .selectedFacets=${getDefaultSelectedFacets()}
327
- .analyticsHandler=${mockAnalyticsHandler}
328
- ></more-facets-content>`,
329
- );
330
-
331
- el.facetKey = 'subject';
332
- el.query = 'large-facets'; // Produces a response with 1100 aggregations (>= 1000)
333
- await el.updateComplete;
334
- await aTimeout(50);
335
-
336
- // Get the pagination component
337
- const pagination = el.shadowRoot?.querySelector(
338
- 'more-facets-pagination',
339
- ) as any;
340
- expect(pagination).to.exist;
341
-
342
- // Simulate clicking page 2
343
- pagination.currentPage = 2;
344
- await pagination.updateComplete;
345
-
346
- // Verify analytics event was sent
347
- expect(mockAnalyticsHandler.callCategory).to.equal('collection-browser');
348
- expect(mockAnalyticsHandler.callAction).to.equal('moreFacetsPageChange');
349
- expect(mockAnalyticsHandler.callLabel).to.equal('2');
350
- });
351
-
352
- it('should render clearable text input for filtering', async () => {
353
- const searchService = new MockSearchService();
354
-
355
- const el = await fixture<MoreFacetsContent>(
356
- html`<more-facets-content
357
- .facetKey=${'year'}
358
- .query=${'more-facets'}
359
- .searchService=${searchService}
360
- .selectedFacets=${yearSelectedFacets}
361
- ></more-facets-content>`,
362
- );
363
-
364
- await el.updateComplete;
365
- await aTimeout(50);
366
-
367
- // Verify the clearable text input component is present
368
- const clearableInput = el.shadowRoot?.querySelector(
369
- 'ia-clearable-text-input',
370
- ) as HTMLElement;
371
- expect(clearableInput).to.exist;
372
- });
373
-
374
- it('should clear filter text when clear event is dispatched', async () => {
375
- const searchService = new MockSearchService();
376
-
377
- const el = await fixture<MoreFacetsContent>(
378
- html`<more-facets-content
379
- .facetKey=${'year'}
380
- .query=${'more-facets'}
381
- .searchService=${searchService}
382
- .selectedFacets=${yearSelectedFacets}
383
- ></more-facets-content>`,
384
- );
385
-
386
- await el.updateComplete;
387
- await aTimeout(50);
388
-
389
- // Simulate typing into the clearable input by dispatching input event
390
- const clearableInput = el.shadowRoot?.querySelector(
391
- 'ia-clearable-text-input',
392
- ) as HTMLElement & { value: string };
393
- expect(clearableInput).to.exist;
394
-
395
- clearableInput.value = 'test';
396
- clearableInput.dispatchEvent(new Event('input'));
397
- await el.updateComplete;
398
-
399
- // Dispatch clear event
400
- clearableInput.dispatchEvent(new CustomEvent('clear', { detail: 'test' }));
401
- await el.updateComplete;
402
-
403
- // Verify the filter was cleared
404
- expect(clearableInput.value).to.equal('');
405
- });
406
-
407
- describe('Modal container height constraint', () => {
408
- // Register a test wrapper element to simulate the modal's scroll container
409
- if (!customElements.get('test-scroll-wrapper')) {
410
- customElements.define(
411
- 'test-scroll-wrapper',
412
- class extends HTMLElement {
413
- constructor() {
414
- super();
415
- this.attachShadow({ mode: 'open' });
416
- this.shadowRoot!.innerHTML = `
417
- <style>
418
- :host { display: block; }
419
- .content { overflow-y: auto; max-height: 300px; }
420
- </style>
421
- <div class="content"><slot></slot></div>
422
- `;
423
- }
424
- },
425
- );
426
- }
427
-
428
- it('should constrain section height when inside a scroll container', async () => {
429
- const el = await fixture<MoreFacetsContent>(html`
430
- <test-scroll-wrapper>
431
- <more-facets-content></more-facets-content>
432
- </test-scroll-wrapper>
433
- `);
434
-
435
- const mfc = el.querySelector('more-facets-content') as MoreFacetsContent;
436
- mfc.facetsLoading = false;
437
- await mfc.updateComplete;
438
-
439
- // Wait for the constrainToScrollContainer rAF callback
440
- await new Promise(r =>
441
- requestAnimationFrame(() => requestAnimationFrame(r)),
442
- );
443
-
444
- const section = mfc.shadowRoot?.querySelector(
445
- 'section#more-facets',
446
- ) as HTMLElement;
447
-
448
- // The section's inline max-height should be set when it would
449
- // overflow the 300px scroll container
450
- const sectionHeight = section.getBoundingClientRect().height;
451
- const wrapper = el.shadowRoot?.querySelector('.content') as HTMLElement;
452
- const wrapperBottom = wrapper.getBoundingClientRect().bottom;
453
- const sectionTop = section.getBoundingClientRect().top;
454
- const availableSpace = wrapperBottom - sectionTop;
455
-
456
- // The section should not exceed the available space in the container
457
- expect(sectionHeight).to.be.at.most(availableSpace + 1); // +1 for rounding
458
- });
459
-
460
- it('should not constrain section when no scroll container exists', async () => {
461
- const el = await fixture<MoreFacetsContent>(
462
- html`<more-facets-content></more-facets-content>`,
463
- );
464
-
465
- el.facetsLoading = false;
466
- await el.updateComplete;
467
-
468
- // Wait for the constrainToScrollContainer rAF callback
469
- await new Promise(r =>
470
- requestAnimationFrame(() => requestAnimationFrame(r)),
471
- );
472
-
473
- const section = el.shadowRoot?.querySelector(
474
- 'section#more-facets',
475
- ) as HTMLElement;
476
-
477
- // No inline max-height should be set when there's no scroll container
478
- expect(section.style.maxHeight).to.equal('');
479
- });
480
- });
481
-
482
- describe('Horizontal scroll navigation arrows', () => {
483
- it('should use scroll-nav-container in horizontal scroll mode', async () => {
484
- const searchService = new MockSearchService();
485
-
486
- const el = await fixture<MoreFacetsContent>(
487
- html`<more-facets-content
488
- .searchService=${searchService}
489
- ></more-facets-content>`,
490
- );
491
-
492
- el.facetKey = 'year';
493
- el.query = 'more-facets'; // Produces < 1000 aggregations
494
- await el.updateComplete;
495
- await aTimeout(50);
496
-
497
- // Verify scroll navigation container exists in horizontal scroll mode
498
- expect(el.shadowRoot?.querySelector('.scroll-nav-container')).to.exist;
499
-
500
- // Verify horizontal container and facets-content exist inside it
501
- expect(
502
- el.shadowRoot?.querySelector(
503
- '.scroll-nav-container .facets-content.horizontal-scroll-mode',
504
- ),
505
- ).to.exist;
506
- expect(
507
- el.shadowRoot?.querySelector(
508
- '.scroll-nav-container .facets-horizontal-container',
509
- ),
510
- ).to.exist;
511
- });
512
-
513
- it('should NOT show scroll arrows in pagination mode', async () => {
514
- const searchService = new MockSearchService();
515
-
516
- const el = await fixture<MoreFacetsContent>(
517
- html`<more-facets-content
518
- .searchService=${searchService}
519
- .selectedFacets=${getDefaultSelectedFacets()}
520
- ></more-facets-content>`,
521
- );
522
-
523
- el.facetKey = 'subject';
524
- el.query = 'large-facets'; // Produces >= 1000 aggregations
525
- await el.updateComplete;
526
- await aTimeout(50);
527
-
528
- // Verify scroll navigation container does NOT exist
529
- expect(el.shadowRoot?.querySelector('.scroll-nav-container')).to.not
530
- .exist;
531
- expect(el.shadowRoot?.querySelector('.scroll-arrow')).to.not.exist;
532
- });
533
-
534
- it('should hide scroll arrows when content does not overflow', async () => {
535
- const searchService = new MockSearchService();
536
-
537
- const el = await fixture<MoreFacetsContent>(
538
- html`<more-facets-content
539
- .searchService=${searchService}
540
- ></more-facets-content>`,
541
- );
542
-
543
- el.facetKey = 'year';
544
- el.query = 'more-facets';
545
- await el.updateComplete;
546
- await aTimeout(50);
547
-
548
- // In test environment, there's no real layout so scrollWidth === clientWidth.
549
- // Arrows should be hidden when there's no horizontal overflow.
550
- expect(el.shadowRoot?.querySelector('.scroll-arrow')).to.not.exist;
551
- });
552
- });
553
231
  });
@@ -112,7 +112,7 @@ describe('More facets pagination', () => {
112
112
 
113
113
  const fake1 = sinon.fake();
114
114
  const fake2 = sinon.fake();
115
- el.updatePages = fake1;
115
+ el.observePageCount = fake1;
116
116
  el.emitPageClick = fake2;
117
117
 
118
118
  // select first page button
@@ -146,7 +146,7 @@ describe('More facets pagination', () => {
146
146
 
147
147
  const fake1 = sinon.fake();
148
148
  const fake2 = sinon.fake();
149
- el.updatePages = fake1;
149
+ el.observePageCount = fake1;
150
150
  el.emitPageClick = fake2;
151
151
 
152
152
  // select first page button
@@ -182,7 +182,7 @@ describe('More facets pagination', () => {
182
182
 
183
183
  const fake1 = sinon.fake();
184
184
  const fake2 = sinon.fake();
185
- el.updatePages = fake1;
185
+ el.observePageCount = fake1;
186
186
  el.emitPageClick = fake2;
187
187
 
188
188
  // select first page button
@@ -198,88 +198,4 @@ describe('More facets pagination', () => {
198
198
  expect(el.currentPage).to.equal(6); // brings us forward 1 page
199
199
  });
200
200
  });
201
-
202
- describe('Compact mode', () => {
203
- it('shows all pages when size <= 3', async () => {
204
- const el = await fixture<MoreFacetsPagination>(
205
- html`<more-facets-pagination
206
- .size=${3}
207
- .compact=${true}
208
- ></more-facets-pagination>`,
209
- );
210
-
211
- await el.updateComplete;
212
- expect(el.pages).to.deep.equal([1, 2, 3]);
213
- });
214
-
215
- it('shows first, prev, current, next, ..., last for middle page', async () => {
216
- const el = await fixture<MoreFacetsPagination>(
217
- html`<more-facets-pagination
218
- .size=${20}
219
- .currentPage=${10}
220
- .compact=${true}
221
- ></more-facets-pagination>`,
222
- );
223
-
224
- await el.updateComplete;
225
- // first, ..., prev, current, next, ..., last
226
- expect(el.pages).to.deep.equal([1, 0, 9, 10, 11, 0, 20]);
227
- });
228
-
229
- it('shows correct pages when on page 1', async () => {
230
- const el = await fixture<MoreFacetsPagination>(
231
- html`<more-facets-pagination
232
- .size=${20}
233
- .currentPage=${1}
234
- .compact=${true}
235
- ></more-facets-pagination>`,
236
- );
237
-
238
- await el.updateComplete;
239
- // first (current), next, ..., last
240
- expect(el.pages).to.deep.equal([1, 2, 0, 20]);
241
- });
242
-
243
- it('shows correct pages when on last page', async () => {
244
- const el = await fixture<MoreFacetsPagination>(
245
- html`<more-facets-pagination
246
- .size=${20}
247
- .currentPage=${20}
248
- .compact=${true}
249
- ></more-facets-pagination>`,
250
- );
251
-
252
- await el.updateComplete;
253
- // first, ..., prev, last (current)
254
- expect(el.pages).to.deep.equal([1, 0, 19, 20]);
255
- });
256
-
257
- it('shows correct pages when on page 2', async () => {
258
- const el = await fixture<MoreFacetsPagination>(
259
- html`<more-facets-pagination
260
- .size=${20}
261
- .currentPage=${2}
262
- .compact=${true}
263
- ></more-facets-pagination>`,
264
- );
265
-
266
- await el.updateComplete;
267
- // first, current, next, ..., last
268
- expect(el.pages).to.deep.equal([1, 2, 3, 0, 20]);
269
- });
270
-
271
- it('shows correct pages when on second-to-last page', async () => {
272
- const el = await fixture<MoreFacetsPagination>(
273
- html`<more-facets-pagination
274
- .size=${20}
275
- .currentPage=${19}
276
- .compact=${true}
277
- ></more-facets-pagination>`,
278
- );
279
-
280
- await el.updateComplete;
281
- // first, ..., prev, current, last
282
- expect(el.pages).to.deep.equal([1, 0, 18, 19, 20]);
283
- });
284
- });
285
201
  });
@@ -128,4 +128,66 @@ describe('Collection Browser Data Source', () => {
128
128
  dataSource.refreshLetterCounts();
129
129
  expect(dataSource.prefixFilterCountMap).to.deep.equal({});
130
130
  });
131
+
132
+ describe('empty FTS query in collection falls back to metadata search', () => {
133
+ it('allows search with empty query and FTS in a collection', async () => {
134
+ const searchService = new MockSearchService();
135
+ host.searchService = searchService;
136
+ host.withinCollection = 'test-collection';
137
+ host.searchType = SearchType.FULLTEXT;
138
+ host.baseQuery = '';
139
+ await host.updateComplete;
140
+
141
+ const dataSource = new CollectionBrowserDataSource(host);
142
+ host.addController(dataSource);
143
+ expect(dataSource.canPerformSearch).to.be.true;
144
+ host.removeController(dataSource);
145
+ });
146
+
147
+ it('uses metadata search type for fetch when FTS query is empty in a collection', async () => {
148
+ const searchService = new MockSearchService();
149
+ host.searchService = searchService;
150
+ host.withinCollection = 'test-collection';
151
+ host.searchType = SearchType.FULLTEXT;
152
+ host.baseQuery = '';
153
+ await host.updateComplete;
154
+
155
+ const dataSource = new CollectionBrowserDataSource(host);
156
+ host.addController(dataSource);
157
+ await dataSource.fetchPage(1);
158
+
159
+ expect(searchService.searchType).to.equal(SearchType.METADATA);
160
+ host.removeController(dataSource);
161
+ });
162
+
163
+ it('uses FTS search type for fetch when FTS query is non-empty in a collection', async () => {
164
+ const searchService = new MockSearchService();
165
+ host.searchService = searchService;
166
+ host.withinCollection = 'test-collection';
167
+ host.searchType = SearchType.FULLTEXT;
168
+ host.baseQuery = 'some query';
169
+ await host.updateComplete;
170
+
171
+ const dataSource = new CollectionBrowserDataSource(host);
172
+ host.addController(dataSource);
173
+ await dataSource.fetchPage(1);
174
+
175
+ expect(searchService.searchType).to.equal(SearchType.FULLTEXT);
176
+ host.removeController(dataSource);
177
+ });
178
+
179
+ it('does not allow search with empty FTS query outside a collection', async () => {
180
+ const searchService = new MockSearchService();
181
+ host.searchService = searchService;
182
+ host.withinCollection = undefined;
183
+ host.searchType = SearchType.FULLTEXT;
184
+ host.baseQuery = '';
185
+ await host.updateComplete;
186
+
187
+ const dataSource = new CollectionBrowserDataSource(host);
188
+ host.addController(dataSource);
189
+ expect(dataSource.canPerformSearch).to.be.false;
190
+ host.removeController(dataSource);
191
+ });
192
+ });
131
193
  });
@@ -1349,54 +1349,6 @@ export const getMockSuccessWithManyAggregations: () => Result<
1349
1349
  },
1350
1350
  });
1351
1351
 
1352
- /**
1353
- * Returns a mock response with 1000+ subject aggregation buckets,
1354
- * used to test pagination mode in the More Facets dialog.
1355
- */
1356
- export const getMockSuccessWithLargeAggregations: () => Result<
1357
- SearchResponse,
1358
- SearchServiceError
1359
- > = () => {
1360
- const buckets = Array.from({ length: 1100 }, (_, i) => ({
1361
- key: `subject-${i}`,
1362
- doc_count: 1100 - i,
1363
- }));
1364
- return {
1365
- success: {
1366
- request: {
1367
- kind: 'aggregations' as const,
1368
- clientParameters: {
1369
- user_query: 'large-facets',
1370
- sort: [],
1371
- },
1372
- backendRequests: {
1373
- primary: {
1374
- kind: 'aggregations' as const,
1375
- finalized_parameters: {
1376
- user_query: 'large-facets',
1377
- sort: [],
1378
- },
1379
- },
1380
- },
1381
- },
1382
- rawResponse: {},
1383
- sessionContext: {},
1384
- response: {
1385
- totalResults: 0,
1386
- returnedCount: 0,
1387
- results: [],
1388
- aggregations: {
1389
- subject: new Aggregation({ buckets }),
1390
- },
1391
- },
1392
- responseHeader: {
1393
- succeeded: true,
1394
- query_time: 0,
1395
- },
1396
- },
1397
- };
1398
- };
1399
-
1400
1352
  export const getMockErrorResult: () => Result<
1401
1353
  SearchResponse,
1402
1354
  SearchServiceError
@@ -33,7 +33,6 @@ import {
33
33
  getMockSuccessNoResults,
34
34
  getMockSuccessWithWebArchiveHits,
35
35
  getMockSuccessWithManyAggregations,
36
- getMockSuccessWithLargeAggregations,
37
36
  getMockSuccessTvFields,
38
37
  getMockSuccessArchiveOrgUserResult,
39
38
  getMockSuccessArchiveOrgUserNoBlurResult,
@@ -65,7 +64,6 @@ const responses: Record<
65
64
  'tv-collection': getMockSuccessForTvCollection,
66
65
  'web-archive': getMockSuccessWithWebArchiveHits,
67
66
  'more-facets': getMockSuccessWithManyAggregations,
68
- 'large-facets': getMockSuccessWithLargeAggregations,
69
67
  'many-fields': getMockSuccessManyFields,
70
68
  'tv-fields': getMockSuccessTvFields,
71
69
  'no-results': getMockSuccessNoResults,
@@ -213,6 +213,23 @@ describe('Tile Dispatcher', () => {
213
213
  await el.updateComplete;
214
214
  expect(el.getHoverPane()).not.to.exist;
215
215
  });
216
+
217
+ it('should not show info button when hover pane is not enabled', async () => {
218
+ const el = await fixture<TileDispatcher>(html`
219
+ <tile-dispatcher
220
+ .tileDisplayMode=${'grid'}
221
+ .model=${{ mediatype: 'texts' }}
222
+ .enableHoverPane=${false}
223
+ >
224
+ </tile-dispatcher>
225
+ `);
226
+
227
+ const itemTile = el.shadowRoot?.querySelector('item-tile') as ItemTile;
228
+ expect(itemTile).to.exist;
229
+
230
+ const infoButton = itemTile.shadowRoot?.querySelector('.info-button');
231
+ expect(infoButton).to.not.exist;
232
+ });
216
233
  });
217
234
 
218
235
  describe('Accessibility', () => {
@@ -45,7 +45,7 @@ describe('Mediatype Icon', () => {
45
45
  expect(iconDiv.title).to.equal('TV');
46
46
  });
47
47
 
48
- it('renders TV Commercial mediatype for TV items with ad ids', async () => {
48
+ it('renders TV Political Ad mediatype for TV items with ad ids', async () => {
49
49
  model.mediatype = 'movies';
50
50
  model.collections = ['tvnews'];
51
51
  model.adIds = ['foo'];
@@ -54,10 +54,10 @@ describe('Mediatype Icon', () => {
54
54
  `);
55
55
 
56
56
  const iconDiv = el.shadowRoot?.querySelector('#icon') as HTMLDivElement;
57
- expect(iconDiv.title).to.equal('TV Commercial');
57
+ expect(iconDiv.title).to.equal('TV Political Ad');
58
58
  });
59
59
 
60
- it('renders TV Commercial mediatype for TV items in tv_ads collection', async () => {
60
+ it('renders TV Political Ad mediatype for TV items in tv_ads collection', async () => {
61
61
  model.mediatype = 'movies';
62
62
  model.collections = ['tvnews', 'tv_ads'];
63
63
  const el = await fixture<TileMediatypeIcon>(html`
@@ -65,7 +65,7 @@ describe('Mediatype Icon', () => {
65
65
  `);
66
66
 
67
67
  const iconDiv = el.shadowRoot?.querySelector('#icon') as HTMLDivElement;
68
- expect(iconDiv.title).to.equal('TV Commercial');
68
+ expect(iconDiv.title).to.equal('TV Political Ad');
69
69
  });
70
70
 
71
71
  it('renders TV Fact Check mediatype for TV search results with fact check URLs', async () => {