@internetarchive/collection-browser 4.5.0 → 4.5.1-alpha-webdev8221.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 (70) hide show
  1. package/dist/index.js.map +1 -1
  2. package/dist/src/app-root.js +683 -683
  3. package/dist/src/app-root.js.map +1 -1
  4. package/dist/src/collection-browser.d.ts +0 -4
  5. package/dist/src/collection-browser.js +767 -776
  6. package/dist/src/collection-browser.js.map +1 -1
  7. package/dist/src/collection-facets/facet-row.js +143 -143
  8. package/dist/src/collection-facets/facet-row.js.map +1 -1
  9. package/dist/src/collection-facets/facets-template.js +24 -24
  10. package/dist/src/collection-facets/facets-template.js.map +1 -1
  11. package/dist/src/data-source/collection-browser-data-source-interface.d.ts +5 -0
  12. package/dist/src/data-source/collection-browser-data-source-interface.js.map +1 -1
  13. package/dist/src/data-source/collection-browser-data-source.d.ts +1 -1
  14. package/dist/src/data-source/collection-browser-data-source.js +8 -6
  15. package/dist/src/data-source/collection-browser-data-source.js.map +1 -1
  16. package/dist/src/restoration-state-handler.js +2 -4
  17. package/dist/src/restoration-state-handler.js.map +1 -1
  18. package/dist/src/styles/tile-action-styles.js +43 -43
  19. package/dist/src/styles/tile-action-styles.js.map +1 -1
  20. package/dist/src/tiles/base-tile-component.js +11 -11
  21. package/dist/src/tiles/base-tile-component.js.map +1 -1
  22. package/dist/src/tiles/grid/item-tile.js +138 -138
  23. package/dist/src/tiles/grid/item-tile.js.map +1 -1
  24. package/dist/src/tiles/list/tile-list-compact-header.js +62 -62
  25. package/dist/src/tiles/list/tile-list-compact-header.js.map +1 -1
  26. package/dist/src/tiles/list/tile-list-compact.js +123 -123
  27. package/dist/src/tiles/list/tile-list-compact.js.map +1 -1
  28. package/dist/src/tiles/list/tile-list.js +310 -310
  29. package/dist/src/tiles/list/tile-list.js.map +1 -1
  30. package/dist/src/tiles/models.js.map +1 -1
  31. package/dist/src/tiles/tile-dispatcher.js +286 -286
  32. package/dist/src/tiles/tile-dispatcher.js.map +1 -1
  33. package/dist/src/tiles/tile-display-value-provider.js.map +1 -1
  34. package/dist/src/utils/date-filter-field.d.ts +6 -0
  35. package/dist/src/utils/date-filter-field.js +9 -0
  36. package/dist/src/utils/date-filter-field.js.map +1 -0
  37. package/dist/test/collection-browser.test.js +81 -0
  38. package/dist/test/collection-browser.test.js.map +1 -1
  39. package/dist/test/collection-facets/facet-row.test.js +31 -31
  40. package/dist/test/collection-facets/facet-row.test.js.map +1 -1
  41. package/dist/test/tiles/grid/item-tile.test.js +77 -77
  42. package/dist/test/tiles/grid/item-tile.test.js.map +1 -1
  43. package/dist/test/tiles/list/tile-list.test.js +134 -134
  44. package/dist/test/tiles/list/tile-list.test.js.map +1 -1
  45. package/dist/test/tiles/tile-dispatcher.test.js +92 -92
  46. package/dist/test/tiles/tile-dispatcher.test.js.map +1 -1
  47. package/index.ts +29 -29
  48. package/package.json +1 -1
  49. package/src/app-root.ts +1284 -1284
  50. package/src/collection-browser.ts +3165 -3176
  51. package/src/collection-facets/facet-row.ts +309 -309
  52. package/src/collection-facets/facets-template.ts +85 -85
  53. package/src/data-source/collection-browser-data-source-interface.ts +6 -0
  54. package/src/data-source/collection-browser-data-source.ts +16 -7
  55. package/src/restoration-state-handler.ts +5 -5
  56. package/src/styles/tile-action-styles.ts +59 -59
  57. package/src/tiles/base-tile-component.ts +124 -124
  58. package/src/tiles/grid/item-tile.ts +347 -347
  59. package/src/tiles/list/tile-list-compact-header.ts +112 -112
  60. package/src/tiles/list/tile-list-compact.ts +278 -278
  61. package/src/tiles/list/tile-list.ts +718 -718
  62. package/src/tiles/models.ts +21 -21
  63. package/src/tiles/tile-dispatcher.ts +637 -637
  64. package/src/tiles/tile-display-value-provider.ts +133 -133
  65. package/src/utils/date-filter-field.ts +11 -0
  66. package/test/collection-browser.test.ts +132 -0
  67. package/test/collection-facets/facet-row.test.ts +421 -421
  68. package/test/tiles/grid/item-tile.test.ts +520 -520
  69. package/test/tiles/list/tile-list.test.ts +576 -576
  70. package/test/tiles/tile-dispatcher.test.ts +320 -320
@@ -1,133 +1,133 @@
1
- import { TemplateResult, html, nothing } from 'lit';
2
- import { msg, str } from '@lit/localize';
3
- import type { SortParam } from '@internetarchive/search-service';
4
- import type { TileModel } from '../models';
5
- import { formatDate } from '../utils/format-date';
6
-
7
- /**
8
- * A class encapsulating shared logic for converting model values into display values
9
- * across different types of tiles.
10
- */
11
- export class TileDisplayValueProvider {
12
- private model?: TileModel;
13
-
14
- private baseNavigationUrl?: string;
15
-
16
- private collectionPagePath?: string;
17
-
18
- private sortParam?: SortParam;
19
-
20
- private creatorFilter?: string;
21
-
22
- constructor(
23
- options: {
24
- model?: TileModel;
25
- baseNavigationUrl?: string;
26
- collectionPagePath?: string;
27
- sortParam?: SortParam;
28
- creatorFilter?: string;
29
- } = {},
30
- ) {
31
- this.model = options.model;
32
- this.baseNavigationUrl = options.baseNavigationUrl;
33
- this.collectionPagePath = options.collectionPagePath ?? '/details/';
34
- this.sortParam = options.sortParam;
35
- this.creatorFilter = options.creatorFilter;
36
- }
37
-
38
- /**
39
- * Examines the creator(s) for the given tile model, returning
40
- * the first creator whose name matches the provided filter
41
- * (or simply the first creator overall if no filter is provided).
42
- */
43
- get firstCreatorMatchingFilter(): string | undefined {
44
- let matchingCreator;
45
-
46
- // If we're filtering by creator initial and have multiple creators, we want
47
- // to surface the first creator who matches the filter.
48
- if (this.creatorFilter && this.model?.creators.length) {
49
- const firstLetter = this.creatorFilter; // This is just to satisfy tsc
50
- matchingCreator = this.model.creators.find(creator =>
51
- // Decompose combining characters first, so that e.g., filtering on E matches É too.
52
- // Then remove anything that isn't strictly alphabetic, since our filters currently
53
- // only handle A-Z. The first such letter (if one exists) is what needs to match.
54
- creator
55
- .normalize('NFD')
56
- .replace(/[^A-Z]+/gi, '')
57
- .toUpperCase()
58
- .startsWith(firstLetter),
59
- );
60
- }
61
-
62
- return matchingCreator ?? this.model?.creator;
63
- }
64
-
65
- /**
66
- * The label indicating what year an account item was created.
67
- * E.g., "Archivist since 2015"
68
- */
69
- get accountLabel(): string {
70
- return this.model?.dateAdded
71
- ? msg(str`Archivist since ${this.model.dateAdded.getFullYear()}`)
72
- : '';
73
- }
74
-
75
- /**
76
- * The readable label for the current sort if it is a type of date sort,
77
- * or the empty string otherwise.
78
- */
79
- get dateLabel(): string {
80
- switch (this.sortParam?.field) {
81
- case 'publicdate':
82
- return msg('Archived');
83
- case 'reviewdate':
84
- return msg('Reviewed');
85
- case 'addeddate':
86
- return msg('Added');
87
- case 'date':
88
- return msg('Published');
89
- default:
90
- return '';
91
- }
92
- }
93
-
94
- /**
95
- * The readable label for the current views column, based on whether
96
- * weekly or all-time views are being shown.
97
- */
98
- get viewsLabel(): string {
99
- return this.sortParam?.field === 'week'
100
- ? msg('Weekly views')
101
- : msg('All-time views');
102
- }
103
-
104
- /**
105
- * Produces a URL pointing at the item page for the given identifier,
106
- * using the current base URL and the correct path based on whether the
107
- * item is specified to be a collection (default false).
108
- */
109
- itemPageUrl(
110
- identifier?: string,
111
- isCollection = false,
112
- ): string | typeof nothing {
113
- if (!identifier || this.baseNavigationUrl == null) return nothing;
114
- const basePath = isCollection ? this.collectionPagePath : '/details/';
115
- return `${this.baseNavigationUrl}${basePath}${identifier}`;
116
- }
117
-
118
- /**
119
- * Produces a template for a link to a single web capture of the given URL and date
120
- */
121
- webArchivesCaptureLink(url: string, date: Date): TemplateResult {
122
- // Convert the date into the format used to identify wayback captures (e.g., '20150102124550')
123
- const captureDateStr = date
124
- .toISOString()
125
- .replace(/[TZ:-]/g, '')
126
- .replace(/\..*/, '');
127
- // url must not be percent-encoded — Wayback Machine matches on the raw URL
128
- const captureHref = `https://web.archive.org/web/${captureDateStr}/${url}`;
129
- const captureText = formatDate(date, 'long');
130
-
131
- return html` <a href=${captureHref}> ${captureText} </a> `;
132
- }
133
- }
1
+ import { TemplateResult, html, nothing } from 'lit';
2
+ import { msg, str } from '@lit/localize';
3
+ import type { SortParam } from '@internetarchive/search-service';
4
+ import type { TileModel } from '../models';
5
+ import { formatDate } from '../utils/format-date';
6
+
7
+ /**
8
+ * A class encapsulating shared logic for converting model values into display values
9
+ * across different types of tiles.
10
+ */
11
+ export class TileDisplayValueProvider {
12
+ private model?: TileModel;
13
+
14
+ private baseNavigationUrl?: string;
15
+
16
+ private collectionPagePath?: string;
17
+
18
+ private sortParam?: SortParam;
19
+
20
+ private creatorFilter?: string;
21
+
22
+ constructor(
23
+ options: {
24
+ model?: TileModel;
25
+ baseNavigationUrl?: string;
26
+ collectionPagePath?: string;
27
+ sortParam?: SortParam;
28
+ creatorFilter?: string;
29
+ } = {},
30
+ ) {
31
+ this.model = options.model;
32
+ this.baseNavigationUrl = options.baseNavigationUrl;
33
+ this.collectionPagePath = options.collectionPagePath ?? '/details/';
34
+ this.sortParam = options.sortParam;
35
+ this.creatorFilter = options.creatorFilter;
36
+ }
37
+
38
+ /**
39
+ * Examines the creator(s) for the given tile model, returning
40
+ * the first creator whose name matches the provided filter
41
+ * (or simply the first creator overall if no filter is provided).
42
+ */
43
+ get firstCreatorMatchingFilter(): string | undefined {
44
+ let matchingCreator;
45
+
46
+ // If we're filtering by creator initial and have multiple creators, we want
47
+ // to surface the first creator who matches the filter.
48
+ if (this.creatorFilter && this.model?.creators.length) {
49
+ const firstLetter = this.creatorFilter; // This is just to satisfy tsc
50
+ matchingCreator = this.model.creators.find(creator =>
51
+ // Decompose combining characters first, so that e.g., filtering on E matches É too.
52
+ // Then remove anything that isn't strictly alphabetic, since our filters currently
53
+ // only handle A-Z. The first such letter (if one exists) is what needs to match.
54
+ creator
55
+ .normalize('NFD')
56
+ .replace(/[^A-Z]+/gi, '')
57
+ .toUpperCase()
58
+ .startsWith(firstLetter),
59
+ );
60
+ }
61
+
62
+ return matchingCreator ?? this.model?.creator;
63
+ }
64
+
65
+ /**
66
+ * The label indicating what year an account item was created.
67
+ * E.g., "Archivist since 2015"
68
+ */
69
+ get accountLabel(): string {
70
+ return this.model?.dateAdded
71
+ ? msg(str`Archivist since ${this.model.dateAdded.getFullYear()}`)
72
+ : '';
73
+ }
74
+
75
+ /**
76
+ * The readable label for the current sort if it is a type of date sort,
77
+ * or the empty string otherwise.
78
+ */
79
+ get dateLabel(): string {
80
+ switch (this.sortParam?.field) {
81
+ case 'publicdate':
82
+ return msg('Archived');
83
+ case 'reviewdate':
84
+ return msg('Reviewed');
85
+ case 'addeddate':
86
+ return msg('Added');
87
+ case 'date':
88
+ return msg('Published');
89
+ default:
90
+ return '';
91
+ }
92
+ }
93
+
94
+ /**
95
+ * The readable label for the current views column, based on whether
96
+ * weekly or all-time views are being shown.
97
+ */
98
+ get viewsLabel(): string {
99
+ return this.sortParam?.field === 'week'
100
+ ? msg('Weekly views')
101
+ : msg('All-time views');
102
+ }
103
+
104
+ /**
105
+ * Produces a URL pointing at the item page for the given identifier,
106
+ * using the current base URL and the correct path based on whether the
107
+ * item is specified to be a collection (default false).
108
+ */
109
+ itemPageUrl(
110
+ identifier?: string,
111
+ isCollection = false,
112
+ ): string | typeof nothing {
113
+ if (!identifier || this.baseNavigationUrl == null) return nothing;
114
+ const basePath = isCollection ? this.collectionPagePath : '/details/';
115
+ return `${this.baseNavigationUrl}${basePath}${identifier}`;
116
+ }
117
+
118
+ /**
119
+ * Produces a template for a link to a single web capture of the given URL and date
120
+ */
121
+ webArchivesCaptureLink(url: string, date: Date): TemplateResult {
122
+ // Convert the date into the format used to identify wayback captures (e.g., '20150102124550')
123
+ const captureDateStr = date
124
+ .toISOString()
125
+ .replace(/[TZ:-]/g, '')
126
+ .replace(/\..*/, '');
127
+ // url must not be percent-encoded — Wayback Machine matches on the raw URL
128
+ const captureHref = `https://web.archive.org/web/${captureDateStr}/${url}`;
129
+ const captureText = formatDate(date, 'long');
130
+
131
+ return html` <a href=${captureHref}> ${captureText} </a> `;
132
+ }
133
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Determines the appropriate search filter field name based on date format.
3
+ * Dates in YYYY-MM format (containing a hyphen) use the `date` field;
4
+ * dates in YYYY format use the `year` field.
5
+ */
6
+ export function dateFilterField(
7
+ minDate?: string,
8
+ maxDate?: string,
9
+ ): 'date' | 'year' {
10
+ return minDate?.includes('-') || maxDate?.includes('-') ? 'date' : 'year';
11
+ }
@@ -2504,4 +2504,136 @@ describe('Collection Browser', () => {
2504
2504
  const initialResults = el.dataSource.getAllPages();
2505
2505
  expect(Object.keys(initialResults).length).to.deep.equal(numberOfPages);
2506
2506
  });
2507
+
2508
+ it('uses date filter field for YYYY-MM selected dates', async () => {
2509
+ const searchService = new MockSearchService();
2510
+ const el = await fixture<CollectionBrowser>(
2511
+ html`<collection-browser .searchService=${searchService}>
2512
+ </collection-browser>`,
2513
+ );
2514
+
2515
+ el.baseQuery = 'months';
2516
+ el.searchType = SearchType.TV;
2517
+ el.minSelectedDate = '2001-02';
2518
+ el.maxSelectedDate = '2002-12';
2519
+ await el.updateComplete;
2520
+ await el.initialSearchComplete;
2521
+
2522
+ expect(searchService.searchParams?.filters?.date?.['2001-02']).to.equal(
2523
+ FilterConstraint.GREATER_OR_EQUAL,
2524
+ );
2525
+ expect(searchService.searchParams?.filters?.date?.['2002-12']).to.equal(
2526
+ FilterConstraint.LESS_OR_EQUAL,
2527
+ );
2528
+ expect(searchService.searchParams?.filters?.year).not.to.exist;
2529
+ });
2530
+
2531
+ it('still uses year filter field for YYYY selected dates', async () => {
2532
+ const searchService = new MockSearchService();
2533
+ const el = await fixture<CollectionBrowser>(
2534
+ html`<collection-browser .searchService=${searchService}>
2535
+ </collection-browser>`,
2536
+ );
2537
+
2538
+ el.baseQuery = 'years';
2539
+ el.minSelectedDate = '1950';
2540
+ el.maxSelectedDate = '1970';
2541
+ await el.updateComplete;
2542
+ await el.initialSearchComplete;
2543
+
2544
+ expect(searchService.searchParams?.filters?.year?.['1950']).to.equal(
2545
+ FilterConstraint.GREATER_OR_EQUAL,
2546
+ );
2547
+ expect(searchService.searchParams?.filters?.year?.['1970']).to.equal(
2548
+ FilterConstraint.LESS_OR_EQUAL,
2549
+ );
2550
+ expect(searchService.searchParams?.filters?.date).not.to.exist;
2551
+ });
2552
+
2553
+ it('selects date_histogram for isTVCollection even with metadata searchType', async () => {
2554
+ const searchService = new MockSearchService();
2555
+ const el = await fixture<CollectionBrowser>(
2556
+ html`<collection-browser
2557
+ .searchService=${searchService}
2558
+ .suppressPlaceholders=${true}
2559
+ >
2560
+ </collection-browser>`,
2561
+ );
2562
+
2563
+ el.baseQuery = 'months';
2564
+ el.isTVCollection = true;
2565
+ el.showHistogramDatePicker = true;
2566
+ await el.updateComplete;
2567
+ await el.initialSearchComplete;
2568
+ await nextTick();
2569
+
2570
+ const facets = el.shadowRoot?.querySelector(
2571
+ 'collection-facets',
2572
+ ) as CollectionFacets;
2573
+ await facets?.updateComplete;
2574
+ await nextTick();
2575
+
2576
+ const histogram = facets?.shadowRoot?.querySelector(
2577
+ 'histogram-date-range',
2578
+ ) as HistogramDateRange;
2579
+
2580
+ expect(histogram, 'histogram exists').to.exist;
2581
+ });
2582
+
2583
+ it('uses date: query clause for YYYY-MM date ranges', async () => {
2584
+ const searchService = new MockSearchService();
2585
+ const mockAnalyticsHandler = new MockAnalyticsHandler();
2586
+ const el = await fixture<CollectionBrowser>(
2587
+ html`<collection-browser
2588
+ .searchService=${searchService}
2589
+ .analyticsHandler=${mockAnalyticsHandler}
2590
+ .suppressPlaceholders=${true}
2591
+ >
2592
+ </collection-browser>`,
2593
+ );
2594
+
2595
+ el.baseQuery = 'months';
2596
+ el.searchType = SearchType.TV;
2597
+ el.showHistogramDatePicker = true;
2598
+ await el.updateComplete;
2599
+
2600
+ const facets = el.shadowRoot?.querySelector(
2601
+ 'collection-facets',
2602
+ ) as CollectionFacets;
2603
+ await facets?.updateComplete;
2604
+
2605
+ await nextTick();
2606
+
2607
+ const histogram = facets?.shadowRoot?.querySelector(
2608
+ 'histogram-date-range',
2609
+ ) as HistogramDateRange;
2610
+
2611
+ expect(histogram, 'histogram exists').to.exist;
2612
+
2613
+ // Enter a new min date into the date picker
2614
+ const minDateInput = histogram.shadowRoot?.querySelector(
2615
+ '#date-min',
2616
+ ) as HTMLInputElement;
2617
+
2618
+ const pressEnterEvent = new KeyboardEvent('keyup', {
2619
+ key: 'Enter',
2620
+ });
2621
+
2622
+ minDateInput.value = '2001-02';
2623
+ minDateInput.dispatchEvent(pressEnterEvent);
2624
+
2625
+ // Wait for the histogram's update delay
2626
+ await aTimeout(histogram.updateDelay + 50);
2627
+
2628
+ await el.updateComplete;
2629
+ expect(el.minSelectedDate).to.equal('2001-02');
2630
+ expect(el.maxSelectedDate).to.equal('2002-12');
2631
+
2632
+ // Verify the analytics event used date: not year:
2633
+ expect(mockAnalyticsHandler.callAction).to.equal(
2634
+ analyticsActions.histogramChanged,
2635
+ );
2636
+ expect(mockAnalyticsHandler.callLabel).to.include('date:');
2637
+ expect(mockAnalyticsHandler.callLabel).not.to.include('year:');
2638
+ });
2507
2639
  });