@internetarchive/collection-browser 1.5.3-alpha.1 → 1.6.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.
@@ -57,8 +57,6 @@ import {
57
57
  PrefixFilterCounts,
58
58
  prefixFilterAggregationKeys,
59
59
  FacetEventDetails,
60
- MetadataFieldToSortField,
61
- MetadataSortField,
62
60
  } from './models';
63
61
  import {
64
62
  RestorationStateHandlerInterface,
@@ -102,7 +100,7 @@ export class CollectionBrowser
102
100
 
103
101
  @property({ type: Object }) sortParam: SortParam | null = null;
104
102
 
105
- @property({ type: String }) selectedSort: SortField = SortField.default;
103
+ @property({ type: String }) selectedSort: SortField = SortField.relevance;
106
104
 
107
105
  @property({ type: String }) selectedTitleFilter: string | null = null;
108
106
 
@@ -126,6 +124,8 @@ export class CollectionBrowser
126
124
 
127
125
  @property({ type: String }) collectionPagePath: string = '/details/';
128
126
 
127
+ @property({ type: Object }) collectionInfo?: CollectionExtraInfo;
128
+
129
129
  /** describes where this component is being used */
130
130
  @property({ type: String, reflect: true }) searchContext: string =
131
131
  analyticsCategories.default;
@@ -194,11 +194,6 @@ export class CollectionBrowser
194
194
 
195
195
  @state() private contentWidth?: number;
196
196
 
197
- @state() private defaultSortField: Exclude<SortField, SortField.default> =
198
- SortField.relevance;
199
-
200
- @state() private defaultSortDirection: SortDirection | null = null;
201
-
202
197
  @state() private placeholderType: PlaceholderType = null;
203
198
 
204
199
  @state() private prefixFilterCountMap: Partial<
@@ -356,7 +351,7 @@ export class CollectionBrowser
356
351
  if (sort) {
357
352
  this.sortParam = null;
358
353
  this.sortDirection = null;
359
- this.selectedSort = SortField.default;
354
+ this.selectedSort = SortField.relevance;
360
355
  }
361
356
  }
362
357
 
@@ -558,11 +553,8 @@ export class CollectionBrowser
558
553
  private get sortFilterBarTemplate() {
559
554
  return html`
560
555
  <sort-filter-bar
561
- .defaultSortField=${this.defaultSortField}
562
- .defaultSortDirection=${this.defaultSortDirection}
563
556
  .selectedSort=${this.selectedSort}
564
557
  .sortDirection=${this.sortDirection}
565
- .showRelevance=${this.isRelevanceSortAvailable}
566
558
  .displayMode=${this.displayMode}
567
559
  .selectedTitleFilter=${this.selectedTitleFilter}
568
560
  .selectedCreatorFilter=${this.selectedCreatorFilter}
@@ -606,7 +598,7 @@ export class CollectionBrowser
606
598
  }
607
599
 
608
600
  private selectedSortChanged(): void {
609
- if ([SortField.default, SortField.relevance].includes(this.selectedSort)) {
601
+ if (this.selectedSort === 'relevance') {
610
602
  this.sortParam = null;
611
603
  return;
612
604
  }
@@ -1203,11 +1195,6 @@ export class CollectionBrowser
1203
1195
  this.infiniteScroller.reload();
1204
1196
  }
1205
1197
 
1206
- if (this.withinCollection && this.baseQuery?.trim()) {
1207
- this.defaultSortField = SortField.relevance;
1208
- this.defaultSortDirection = null;
1209
- }
1210
-
1211
1198
  if (!this.initialQueryChangeHappened && this.initialPageNumber > 1) {
1212
1199
  this.scrollToPage(this.initialPageNumber);
1213
1200
  }
@@ -1251,7 +1238,7 @@ export class CollectionBrowser
1251
1238
  this.displayMode = restorationState.displayMode;
1252
1239
  if (restorationState.searchType != null)
1253
1240
  this.searchType = restorationState.searchType;
1254
- this.selectedSort = restorationState.selectedSort ?? SortField.default;
1241
+ this.selectedSort = restorationState.selectedSort ?? SortField.relevance;
1255
1242
  this.sortDirection = restorationState.sortDirection ?? null;
1256
1243
  this.selectedTitleFilter = restorationState.selectedTitleFilter ?? null;
1257
1244
  this.selectedCreatorFilter = restorationState.selectedCreatorFilter ?? null;
@@ -1625,14 +1612,6 @@ export class CollectionBrowser
1625
1612
  });
1626
1613
  }
1627
1614
 
1628
- /**
1629
- * Whether sorting by relevance makes sense for the current state.
1630
- * Currently equivalent to having a non-empty query.
1631
- */
1632
- private get isRelevanceSortAvailable(): boolean {
1633
- return !!this.baseQuery?.trim();
1634
- }
1635
-
1636
1615
  /**
1637
1616
  * Whether a search may be performed in the current state of the component.
1638
1617
  * This is only true if the search service is defined, and either
@@ -1777,9 +1756,7 @@ export class CollectionBrowser
1777
1756
  this.totalResults = success.response.totalResults;
1778
1757
 
1779
1758
  if (this.withinCollection) {
1780
- // For collections, we want the UI to respect the default sort option
1781
- // which can be specified in metadata, or otherwise assumed to be week:desc
1782
- this.applyDefaultCollectionSort(success.response.collectionExtraInfo);
1759
+ this.collectionInfo = success.response.collectionExtraInfo;
1783
1760
  }
1784
1761
 
1785
1762
  const { results, collectionTitles } = success.response;
@@ -1827,39 +1804,6 @@ export class CollectionBrowser
1827
1804
  this.collectionNameCache?.preloadIdentifiers(collectionIdsArray);
1828
1805
  }
1829
1806
 
1830
- /**
1831
- * Applies any default sort option for the current collection, by checking
1832
- * for one in the collection's metadata. If none is found, defaults to sorting
1833
- * descending by weekly views.
1834
- */
1835
- private applyDefaultCollectionSort(collectionInfo?: CollectionExtraInfo) {
1836
- if (this.baseQuery) {
1837
- // If there's a query set, then we default to relevance sorting regardless of
1838
- // the collection metadata-specified sort.
1839
- this.defaultSortField = SortField.relevance;
1840
- this.defaultSortDirection = null;
1841
- return;
1842
- }
1843
-
1844
- const defaultSort: string =
1845
- collectionInfo?.public_metadata?.['sort-by'] ?? '-week';
1846
-
1847
- // Account for both -field and field:dir formats
1848
- let [field, dir] = defaultSort.split(':');
1849
- if (field.startsWith('-')) {
1850
- field = field.slice(1);
1851
- dir = 'desc';
1852
- } else if (!['asc', 'desc'].includes(dir)) {
1853
- dir = 'asc';
1854
- }
1855
-
1856
- const sortField = MetadataFieldToSortField[field as MetadataSortField];
1857
- if (sortField && sortField !== SortField.default) {
1858
- this.defaultSortField = sortField;
1859
- this.defaultSortDirection = dir as SortDirection;
1860
- }
1861
- }
1862
-
1863
1807
  /**
1864
1808
  * This is useful for determining whether we need to reload the scroller.
1865
1809
  *
package/src/models.ts CHANGED
@@ -55,7 +55,6 @@ export type CollectionBrowserContext = 'collection' | 'search';
55
55
  * The sort fields shown in the sort filter bar
56
56
  */
57
57
  export enum SortField {
58
- 'default' = 'default',
59
58
  'relevance' = 'relevance',
60
59
  'alltimeview' = 'alltimeview',
61
60
  'weeklyview' = 'weeklyview',
@@ -88,7 +87,6 @@ export type URLSortField = MetadataSortField | 'title' | 'creator';
88
87
  export const SortFieldDisplayName: {
89
88
  [key in SortField]: string;
90
89
  } = {
91
- default: '', // Use the default sorting option for the current page context, if none has been selected
92
90
  relevance: 'Relevance',
93
91
  alltimeview: 'All-time views',
94
92
  weeklyview: 'Weekly views',
@@ -101,10 +99,9 @@ export const SortFieldDisplayName: {
101
99
  };
102
100
 
103
101
  export const DefaultSortDirection: {
104
- [key in SortField]: SortDirection | null;
102
+ [key in SortField]: SortDirection;
105
103
  } = {
106
- default: null,
107
- relevance: null, // Can't actually change the sort direction for relevance
104
+ relevance: 'desc', // Can't actually change the sort direction for relevance
108
105
  alltimeview: 'desc',
109
106
  weeklyview: 'desc',
110
107
  title: 'asc',
@@ -121,7 +118,6 @@ export const DefaultSortDirection: {
121
118
  export const SortFieldToMetadataField: {
122
119
  [key in SortField]: MetadataSortField | null;
123
120
  } = {
124
- default: null,
125
121
  relevance: null,
126
122
  alltimeview: 'downloads',
127
123
  weeklyview: 'week',
@@ -42,20 +42,11 @@ export class SortFilterBar
42
42
  /** Which display mode the tiles are being rendered with (grid/list-detail/list-compact) */
43
43
  @property({ type: String }) displayMode?: CollectionDisplayMode;
44
44
 
45
- /** The default sort direction to use if none is set */
46
- @property({ type: String }) defaultSortDirection: SortDirection | null = null;
47
-
48
- /** The default sort field to use if none is set */
49
- @property({ type: String }) defaultSortField: Exclude<
50
- SortField,
51
- SortField.default
52
- > = SortField.relevance;
53
-
54
45
  /** The current sort direction (asc/desc), or null if none is set */
55
46
  @property({ type: String }) sortDirection: SortDirection | null = null;
56
47
 
57
48
  /** The field currently being sorted on (e.g., 'title'). Defaults to relevance. */
58
- @property({ type: String }) selectedSort: SortField = SortField.default;
49
+ @property({ type: String }) selectedSort: SortField = SortField.relevance;
59
50
 
60
51
  /** The currently selected title letter filter, or null if none is set */
61
52
  @property({ type: String }) selectedTitleFilter: string | null = null;
@@ -153,7 +144,7 @@ export class SortFilterBar
153
144
  }
154
145
 
155
146
  if (changed.has('selectedSort') && this.sortDirection === null) {
156
- this.sortDirection = DefaultSortDirection[this.finalizedSortField];
147
+ this.sortDirection = DefaultSortDirection[this.selectedSort];
157
148
  }
158
149
 
159
150
  if (changed.has('selectedTitleFilter') && this.selectedTitleFilter) {
@@ -274,8 +265,8 @@ export class SortFilterBar
274
265
  return html`
275
266
  <button
276
267
  class="sort-direction-selector"
277
- ?disabled=${this.finalizedSortField === SortField.relevance}
278
- @click=${this.handleSortDirectionClicked}
268
+ ?disabled=${this.selectedSort === 'relevance'}
269
+ @click=${this.toggleSortDirection}
279
270
  >
280
271
  <span class="sr-only">${srLabel}</span>
281
272
  ${this.sortDirectionIcon}
@@ -286,14 +277,14 @@ export class SortFilterBar
286
277
  /** Template to render the sort direction button's icon in the correct current state */
287
278
  private get sortDirectionIcon(): TemplateResult {
288
279
  // For relevance sort, show a fully disabled icon
289
- if (this.finalizedSortField === SortField.relevance) {
280
+ if (this.selectedSort === 'relevance') {
290
281
  return html`<div class="sort-direction-icon">${sortDisabledIcon}</div>`;
291
282
  }
292
283
 
293
284
  // For all other sorts, show the ascending/descending direction
294
285
  return html`
295
286
  <div class="sort-direction-icon">
296
- ${this.finalizedSortDirection === 'asc' ? sortUpIcon : sortDownIcon}
287
+ ${this.sortDirection === 'asc' ? sortUpIcon : sortDownIcon}
297
288
  </div>
298
289
  `;
299
290
  }
@@ -306,25 +297,25 @@ export class SortFilterBar
306
297
  class=${this.mobileSelectorVisible ? 'hidden' : 'visible'}
307
298
  >
308
299
  <ul id="desktop-sort-selector">
309
- ${this.showRelevance
310
- ? html`<li>
311
- ${this.getSortDisplayOption(SortField.relevance, {
300
+ <li>
301
+ ${this.showRelevance
302
+ ? this.getSortDisplayOption(SortField.relevance, {
312
303
  onClick: () => {
313
304
  this.dropdownBackdropVisible = false;
314
- if (this.finalizedSortField !== SortField.relevance) {
305
+ if (this.selectedSort !== SortField.relevance) {
315
306
  this.clearAlphaBarFilters();
316
307
  this.setSelectedSort(SortField.relevance);
317
308
  }
318
309
  },
319
- })}
320
- </li>`
321
- : nothing}
310
+ })
311
+ : nothing}
312
+ </li>
322
313
  <li>${this.viewsDropdownTemplate}</li>
323
314
  <li>
324
315
  ${this.getSortDisplayOption(SortField.title, {
325
316
  onClick: () => {
326
317
  this.dropdownBackdropVisible = false;
327
- if (this.finalizedSortField !== SortField.title) {
318
+ if (this.selectedSort !== SortField.title) {
328
319
  this.alphaSelectorVisible = 'title';
329
320
  this.selectedCreatorFilter = null;
330
321
  this.setSelectedSort(SortField.title);
@@ -338,7 +329,7 @@ export class SortFilterBar
338
329
  ${this.getSortDisplayOption(SortField.creator, {
339
330
  onClick: () => {
340
331
  this.dropdownBackdropVisible = false;
341
- if (this.finalizedSortField !== SortField.creator) {
332
+ if (this.selectedSort !== SortField.creator) {
342
333
  this.alphaSelectorVisible = 'creator';
343
334
  this.selectedTitleFilter = null;
344
335
  this.setSelectedSort(SortField.creator);
@@ -354,24 +345,19 @@ export class SortFilterBar
354
345
 
355
346
  /** The template to render all the sort options in mobile view */
356
347
  private get mobileSortSelectorTemplate() {
357
- const isDisplayableField = (field: string) =>
358
- field !== SortField.default &&
359
- (field !== SortField.relevance || this.showRelevance);
360
-
361
348
  return html`
362
349
  <div
363
350
  id="mobile-sort-container"
364
351
  class=${this.mobileSelectorVisible ? 'visible' : 'hidden'}
365
352
  >
366
353
  ${this.getSortDropdown({
367
- displayName: html`${SortFieldDisplayName[this.finalizedSortField] ??
368
- 'Relevance'}`,
354
+ displayName: html`${SortFieldDisplayName[this.selectedSort] ?? ''}`,
369
355
  id: 'mobile-dropdown',
370
356
  selected: true,
371
- dropdownOptions: Object.keys(SortField)
372
- .filter(field => isDisplayableField(field))
373
- .map(field => this.getDropdownOption(field as SortField)),
374
- selectedOption: this.finalizedSortField,
357
+ dropdownOptions: Object.keys(SortField).map(field =>
358
+ this.getDropdownOption(field as SortField)
359
+ ),
360
+ selectedOption: this.selectedSort ?? SortField.relevance,
375
361
  onOptionSelected: this.mobileSortChanged,
376
362
  onDropdownClick: () => {
377
363
  this.dropdownBackdropVisible = this.mobileDropdown.open;
@@ -406,8 +392,7 @@ export class SortFilterBar
406
392
  onClick?: (e: Event) => void;
407
393
  }
408
394
  ): TemplateResult {
409
- const isSelected =
410
- options?.selected ?? this.finalizedSortField === sortField;
395
+ const isSelected = options?.selected ?? this.selectedSort === sortField;
411
396
  const displayName = options?.displayName ?? SortFieldDisplayName[sortField];
412
397
  return html`
413
398
  <button
@@ -668,25 +653,7 @@ export class SortFilterBar
668
653
 
669
654
  /** Toggles the current sort direction between 'asc' and 'desc' */
670
655
  private toggleSortDirection() {
671
- this.setSortDirection(
672
- this.finalizedSortDirection === 'desc' ? 'asc' : 'desc'
673
- );
674
- }
675
-
676
- private handleSortDirectionClicked(): void {
677
- if (
678
- !this.sortDirection &&
679
- this.defaultSortField &&
680
- this.defaultSortDirection
681
- ) {
682
- // When the sort direction is merely defaulted (not set by the user), clicking
683
- // the toggled button should "promote" the default sort to an explicitly-set one
684
- // and then toggle it as usual.
685
- this.selectedSort = this.defaultSortField;
686
- this.sortDirection = this.defaultSortDirection;
687
- }
688
-
689
- this.toggleSortDirection();
656
+ this.setSortDirection(this.sortDirection === 'desc' ? 'asc' : 'desc');
690
657
  }
691
658
 
692
659
  private setSelectedSort(sort: SortField) {
@@ -696,20 +663,6 @@ export class SortFilterBar
696
663
  this.emitSortChangedEvent();
697
664
  }
698
665
 
699
- /** The current sort field, or the default one if no explicit sort is set */
700
- private get finalizedSortField(): SortField {
701
- return this.selectedSort === SortField.default
702
- ? this.defaultSortField
703
- : this.selectedSort;
704
- }
705
-
706
- /** The current sort direction, or the default one if no explicit direction is set */
707
- private get finalizedSortDirection(): SortDirection | null {
708
- return this.sortDirection === null
709
- ? this.defaultSortDirection
710
- : this.sortDirection;
711
- }
712
-
713
666
  /**
714
667
  * There are four date sort options.
715
668
  *
@@ -727,7 +680,7 @@ export class SortFilterBar
727
680
  SortField.datereviewed,
728
681
  SortField.dateadded,
729
682
  ];
730
- return dateSortFields.includes(this.finalizedSortField);
683
+ return dateSortFields.includes(this.selectedSort);
731
684
  }
732
685
 
733
686
  /**
@@ -745,7 +698,7 @@ export class SortFilterBar
745
698
  SortField.alltimeview,
746
699
  SortField.weeklyview,
747
700
  ];
748
- return viewSortFields.includes(this.finalizedSortField);
701
+ return viewSortFields.includes(this.selectedSort);
749
702
  }
750
703
 
751
704
  /**
@@ -759,7 +712,7 @@ export class SortFilterBar
759
712
  private get dateSortField(): string {
760
713
  const defaultSort = SortFieldDisplayName[SortField.date];
761
714
  const name = this.dateOptionSelected
762
- ? SortFieldDisplayName[this.finalizedSortField] ?? defaultSort
715
+ ? SortFieldDisplayName[this.selectedSort] ?? defaultSort
763
716
  : defaultSort;
764
717
  return name;
765
718
  }
@@ -775,7 +728,7 @@ export class SortFilterBar
775
728
  private get viewSortField(): string {
776
729
  const defaultSort = SortFieldDisplayName[SortField.weeklyview];
777
730
  const name = this.viewOptionSelected
778
- ? SortFieldDisplayName[this.finalizedSortField] ?? defaultSort
731
+ ? SortFieldDisplayName[this.selectedSort] ?? defaultSort
779
732
  : defaultSort;
780
733
  return name;
781
734
  }
@@ -20,7 +20,6 @@ import { analyticsCategories } from '../src/utils/analytics-events';
20
20
  import type { TileDispatcher } from '../src/tiles/tile-dispatcher';
21
21
  import type { CollectionFacets } from '../src/collection-facets';
22
22
  import type { EmptyPlaceholder } from '../src/empty-placeholder';
23
- import type { SortFilterBar } from '../src/sort-filter-bar/sort-filter-bar';
24
23
 
25
24
  /**
26
25
  * Wait for the next tick of the event loop.
@@ -94,7 +93,7 @@ describe('Collection Browser', () => {
94
93
  el.clearFilters({ sort: true }); // Sort is reset too due to the option
95
94
 
96
95
  expect(el.selectedFacets).to.deep.equal(getDefaultSelectedFacets());
97
- expect(el.selectedSort).to.equal(SortField.default);
96
+ expect(el.selectedSort).to.equal('relevance');
98
97
  expect(el.sortDirection).to.be.null;
99
98
  expect(el.sortParam).to.be.null;
100
99
  expect(el.selectedCreatorFilter).to.be.null;
@@ -764,7 +763,7 @@ describe('Collection Browser', () => {
764
763
  </collection-browser>`
765
764
  );
766
765
 
767
- expect(el.selectedSort).to.equal(SortField.default);
766
+ expect(el.selectedSort).to.equal(SortField.relevance);
768
767
 
769
768
  el.baseQuery = 'foo';
770
769
  await el.updateComplete;
@@ -989,82 +988,6 @@ describe('Collection Browser', () => {
989
988
  ).to.equal('/details/foo?q=%22quoted+query%22');
990
989
  });
991
990
 
992
- it('sets default sort from collection metadata', async () => {
993
- const searchService = new MockSearchService();
994
- const el = await fixture<CollectionBrowser>(
995
- html`<collection-browser
996
- .searchService=${searchService}
997
- .baseNavigationUrl=${''}
998
- ></collection-browser>`
999
- );
1000
-
1001
- el.withinCollection = 'default-sort';
1002
- await el.updateComplete;
1003
- await el.initialSearchComplete;
1004
- await el.updateComplete;
1005
- await aTimeout(50);
1006
-
1007
- const sortBar = el.shadowRoot?.querySelector(
1008
- 'sort-filter-bar'
1009
- ) as SortFilterBar;
1010
- expect(sortBar).to.exist;
1011
- expect(sortBar.defaultSortField).to.equal(SortField.title);
1012
- expect(sortBar.defaultSortDirection).to.equal('asc');
1013
- expect(sortBar.selectedSort).to.equal(SortField.default);
1014
- expect(sortBar.sortDirection).to.be.null;
1015
- });
1016
-
1017
- it('sets default sort from collection metadata in "-field" format', async () => {
1018
- const searchService = new MockSearchService();
1019
- const el = await fixture<CollectionBrowser>(
1020
- html`<collection-browser
1021
- .searchService=${searchService}
1022
- .baseNavigationUrl=${''}
1023
- ></collection-browser>`
1024
- );
1025
-
1026
- el.withinCollection = 'default-sort-concise';
1027
- await el.updateComplete;
1028
- await el.initialSearchComplete;
1029
- await el.updateComplete;
1030
- await aTimeout(50);
1031
-
1032
- const sortBar = el.shadowRoot?.querySelector(
1033
- 'sort-filter-bar'
1034
- ) as SortFilterBar;
1035
- expect(sortBar).to.exist;
1036
- expect(sortBar.defaultSortField).to.equal(SortField.dateadded);
1037
- expect(sortBar.defaultSortDirection).to.equal('desc');
1038
- expect(sortBar.selectedSort).to.equal(SortField.default);
1039
- expect(sortBar.sortDirection).to.be.null;
1040
- });
1041
-
1042
- it('uses relevance sort as default when a query is set', async () => {
1043
- const searchService = new MockSearchService();
1044
- const el = await fixture<CollectionBrowser>(
1045
- html`<collection-browser
1046
- .searchService=${searchService}
1047
- .baseNavigationUrl=${''}
1048
- ></collection-browser>`
1049
- );
1050
-
1051
- el.withinCollection = 'default-sort';
1052
- el.baseQuery = 'default-sort';
1053
- await el.updateComplete;
1054
- await el.initialSearchComplete;
1055
- await el.updateComplete;
1056
- await aTimeout(50);
1057
-
1058
- const sortBar = el.shadowRoot?.querySelector(
1059
- 'sort-filter-bar'
1060
- ) as SortFilterBar;
1061
- expect(sortBar).to.exist;
1062
- expect(sortBar.defaultSortField).to.equal(SortField.relevance);
1063
- expect(sortBar.defaultSortDirection).to.be.null;
1064
- expect(sortBar.selectedSort).to.equal(SortField.default);
1065
- expect(sortBar.sortDirection).to.be.null;
1066
- });
1067
-
1068
991
  it('scrolls to page', async () => {
1069
992
  const searchService = new MockSearchService();
1070
993
  const el = await fixture<CollectionBrowser>(