@internetarchive/collection-browser 1.14.9-alpha1 → 1.14.9-alpha10

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 (33) hide show
  1. package/dist/src/app-root.d.ts +1 -0
  2. package/dist/src/app-root.js +154 -82
  3. package/dist/src/app-root.js.map +1 -1
  4. package/dist/src/collection-browser.d.ts +2 -2
  5. package/dist/src/collection-browser.js +23 -18
  6. package/dist/src/collection-browser.js.map +1 -1
  7. package/dist/src/collection-facets/facet-row.d.ts +30 -0
  8. package/dist/src/collection-facets/facet-row.js +245 -0
  9. package/dist/src/collection-facets/facet-row.js.map +1 -0
  10. package/dist/src/collection-facets/facets-template.d.ts +1 -4
  11. package/dist/src/collection-facets/facets-template.js +40 -182
  12. package/dist/src/collection-facets/facets-template.js.map +1 -1
  13. package/dist/src/models.d.ts +18 -2
  14. package/dist/src/models.js.map +1 -1
  15. package/dist/test/collection-browser.test.js +36 -4
  16. package/dist/test/collection-browser.test.js.map +1 -1
  17. package/dist/test/collection-facets/facet-row.test.d.ts +1 -0
  18. package/dist/test/collection-facets/facet-row.test.js +235 -0
  19. package/dist/test/collection-facets/facet-row.test.js.map +1 -0
  20. package/dist/test/collection-facets/facets-template.test.js +65 -96
  21. package/dist/test/collection-facets/facets-template.test.js.map +1 -1
  22. package/dist/test/collection-facets.test.js +39 -70
  23. package/dist/test/collection-facets.test.js.map +1 -1
  24. package/package.json +2 -2
  25. package/src/app-root.ts +159 -82
  26. package/src/collection-browser.ts +23 -20
  27. package/src/collection-facets/facet-row.ts +274 -0
  28. package/src/collection-facets/facets-template.ts +49 -196
  29. package/src/models.ts +18 -2
  30. package/test/collection-browser.test.ts +36 -4
  31. package/test/collection-facets/facet-row.test.ts +328 -0
  32. package/test/collection-facets/facets-template.test.ts +72 -110
  33. package/test/collection-facets.test.ts +69 -101
@@ -1 +1 @@
1
- {"version":3,"file":"facets-template.js","sourceRoot":"","sources":["../../../src/collection-facets/facets-template.ts"],"names":[],"mappings":"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","sourcesContent":["import { css, html, LitElement, TemplateResult, CSSResultGroup } from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { repeat } from 'lit/directives/repeat.js';\nimport type { CollectionNameCacheInterface } from '@internetarchive/collection-name-cache';\nimport eyeIcon from '../assets/img/icons/eye';\nimport eyeClosedIcon from '../assets/img/icons/eye-closed';\nimport {\n FacetGroup,\n FacetOption,\n FacetBucket,\n SelectedFacets,\n getDefaultSelectedFacets,\n FacetEventDetails,\n FacetState,\n} from '../models';\n\n@customElement('facets-template')\nexport class FacetsTemplate extends LitElement {\n @property({ type: Object }) facetGroup?: FacetGroup;\n\n @property({ type: Object }) selectedFacets?: SelectedFacets;\n\n @property({ type: String }) renderOn?: string;\n\n @property({ type: String }) collectionPagePath: string = '/details/';\n\n @property({ type: Object })\n collectionNameCache?: CollectionNameCacheInterface;\n\n private facetClicked(e: Event, count: number, negative: boolean) {\n const target = e.target as HTMLInputElement;\n const { checked, name, value } = target;\n if (checked) {\n this.facetChecked(name as FacetOption, value, count, negative);\n } else {\n this.facetUnchecked(name as FacetOption, value);\n }\n\n this.dispatchFacetClickEvent(\n name as FacetOption,\n this.getFacetState(checked, negative),\n negative\n );\n }\n\n private facetChecked(\n key: FacetOption,\n value: string,\n count: number,\n negative: boolean\n ) {\n const { selectedFacets } = this;\n let newFacets: SelectedFacets;\n if (selectedFacets) {\n newFacets = {\n ...selectedFacets,\n };\n } else {\n newFacets = getDefaultSelectedFacets();\n }\n newFacets[key][value] = {\n state: this.getFacetState(true, negative),\n count,\n } as FacetBucket;\n\n this.selectedFacets = newFacets;\n this.dispatchSelectedFacetsChanged();\n }\n\n private facetUnchecked(key: FacetOption, value: string) {\n const { selectedFacets } = this;\n let newFacets: SelectedFacets;\n if (selectedFacets) {\n newFacets = {\n ...selectedFacets,\n };\n } else {\n newFacets = getDefaultSelectedFacets();\n }\n delete newFacets[key][value];\n\n this.selectedFacets = newFacets;\n this.dispatchSelectedFacetsChanged();\n }\n\n /** Returns the composed facet state corresponding to a positive or negative facet's checked state */\n private getFacetState(checked: boolean, negative: boolean): FacetState {\n let state: FacetState;\n if (checked) {\n state = negative ? 'hidden' : 'selected';\n } else {\n state = 'none';\n }\n return state;\n }\n\n private dispatchFacetClickEvent(\n key: FacetOption,\n state: FacetState,\n negative: boolean\n ) {\n const event = new CustomEvent<FacetEventDetails>('facetClick', {\n detail: { key, state, negative },\n composed: true,\n });\n this.dispatchEvent(event);\n }\n\n private dispatchSelectedFacetsChanged() {\n const event = new CustomEvent<SelectedFacets>('selectedFacetsChanged', {\n detail: this.selectedFacets,\n bubbles: true,\n composed: true,\n });\n this.dispatchEvent(event);\n }\n\n private getFacetsTemplate(facetGroup: FacetGroup): TemplateResult {\n let facetsBucket = facetGroup?.buckets as FacetBucket[];\n\n /**\n * sorting FacetBucket before render page / modal\n * - first, selected items should be at top having sorted\n * - second, suppressed/hidden items should be after selected having sorted\n * - and then no-selected / not suppressed items should render having sorted\n */\n facetsBucket = [\n ...facetsBucket\n .filter(x => x.state === 'selected')\n .sort((a, b) => (a.count < b.count ? 1 : -1)),\n ...facetsBucket\n .filter(x => x.state === 'hidden')\n .sort((a, b) => (a.count < b.count ? 1 : -1)),\n ...facetsBucket.filter(x => x.state === 'none'),\n ];\n\n return html`\n <div class=\"facets-on-${this.renderOn}\">\n ${repeat(\n facetsBucket,\n bucket => `${facetGroup.key}:${bucket.key}`,\n bucket => {\n const showOnlyCheckboxId = `${facetGroup.key}:${bucket.key}-show-only`;\n const negativeCheckboxId = `${facetGroup.key}:${bucket.key}-negative`;\n // for collections, we need to asynchronously load the collection name\n // so we use the `async-collection-name` widget and for the rest, we have\n // a static value to use\n const bucketTextDisplay =\n facetGroup.key !== 'collection'\n ? html`${bucket.displayText ?? bucket.key}`\n : html`<a href=\"${this.collectionPagePath}${bucket.key}\">\n <async-collection-name\n .collectionNameCache=${this.collectionNameCache}\n .identifier=${bucket.key}\n placeholder=\"-\"\n ></async-collection-name>\n </a> `;\n const facetHidden = bucket.state === 'hidden';\n const facetSelected = bucket.state === 'selected';\n const titleText = `${facetGroup.key}: ${\n bucket.displayText ?? bucket.key\n }`;\n const onlyShowText = facetSelected\n ? `Show all ${facetGroup.key}s`\n : `Only show ${titleText}`;\n const hideText = `Hide ${titleText}`;\n const unhideText = `Unhide ${titleText}`;\n const showHideText = facetHidden ? unhideText : hideText;\n const ariaLabel = `${titleText}, ${bucket.count} results`;\n return html`\n <div class=\"facet-row\">\n <div class=\"facet-checkbox\">\n <input\n type=\"checkbox\"\n .name=${facetGroup.key}\n .value=${bucket.key}\n @click=${(e: Event) => {\n this.facetClicked(e, bucket.count, false);\n }}\n .checked=${facetSelected}\n class=\"select-facet-checkbox\"\n title=${onlyShowText}\n id=${showOnlyCheckboxId}\n />\n <input\n type=\"checkbox\"\n id=${negativeCheckboxId}\n .name=${facetGroup.key}\n .value=${bucket.key}\n @click=${(e: Event) => {\n this.facetClicked(e, bucket.count, true);\n }}\n .checked=${facetHidden}\n class=\"hide-facet-checkbox\"\n />\n <label\n for=${negativeCheckboxId}\n class=\"hide-facet-icon${facetHidden ? ' active' : ''}\"\n title=${showHideText}\n >\n <span class=\"eye\">${eyeIcon}</span>\n <span class=\"eye-closed\">${eyeClosedIcon}</span>\n </label>\n </div>\n <label\n for=${showOnlyCheckboxId}\n class=\"facet-info-display\"\n title=${onlyShowText}\n aria-label=${ariaLabel}\n >\n <div class=\"facet-title\">${bucketTextDisplay}</div>\n <div class=\"facet-count\">\n ${bucket.count.toLocaleString()}\n </div>\n </label>\n </div>\n `;\n }\n )}\n </div>\n `;\n }\n\n render() {\n return html`${this.getFacetsTemplate(this.facetGroup as FacetGroup)}`;\n }\n\n static get styles(): CSSResultGroup {\n return css`\n @media (max-width: 560px) {\n .facets-on-modal {\n column-count: 1 !important;\n }\n }\n .facets-on-modal {\n column-gap: 15px;\n column-count: 3;\n }\n async-collection-name {\n display: contents;\n }\n ul.facet-list {\n list-style: none;\n margin: 0;\n padding: 0;\n }\n ul.facet-list li {\n margin-bottom: 0.2rem;\n display: grid;\n }\n .facet-checkbox {\n margin: 0 5px 0 0;\n display: flex;\n height: 15px;\n }\n .facet-checkbox input:first-child {\n margin-right: 5px;\n }\n .facet-checkbox input {\n height: 15px;\n width: 15px;\n margin: 0;\n }\n .facet-row {\n display: flex;\n font-weight: 500;\n font-size: 1.2rem;\n margin: 2.5px auto;\n height: auto;\n border-top: var(--facet-row-border-top, 1px solid transparent);\n border-bottom: var(--facet-row-border-bottom, 1px solid transparent);\n overflow: hidden;\n }\n .facet-info-display {\n display: flex;\n flex: 1 1 0%;\n cursor: pointer;\n flex-wrap: wrap;\n }\n .facet-title {\n word-break: break-word;\n display: inline-block;\n flex: 1 1 0%;\n }\n .facet-count {\n text-align: right;\n }\n .select-facet-checkbox {\n cursor: pointer;\n display: inline-block;\n }\n .hide-facet-checkbox {\n display: none;\n }\n .hide-facet-icon {\n width: 15px;\n height: 15px;\n cursor: pointer;\n opacity: 0.3;\n display: inline-block;\n }\n .hide-facet-icon:hover,\n .active {\n opacity: 1;\n }\n .hide-facet-icon:hover .eye,\n .hide-facet-icon .eye-closed {\n display: none;\n }\n .hide-facet-icon:hover .eye-closed,\n .hide-facet-icon.active .eye-closed {\n display: inline;\n }\n .hide-facet-icon.active .eye {\n display: none;\n }\n .sorting-icon {\n cursor: pointer;\n }\n\n a:link,\n a:visited {\n text-decoration: none;\n color: var(--ia-theme-link-color, #4b64ff);\n }\n a:hover {\n text-decoration: underline;\n }\n `;\n }\n}\n"]}
1
+ {"version":3,"file":"facets-template.js","sourceRoot":"","sources":["../../../src/collection-facets/facets-template.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n css,\n html,\n LitElement,\n TemplateResult,\n CSSResultGroup,\n nothing,\n} from 'lit';\nimport { customElement, property } from 'lit/decorators.js';\nimport { repeat } from 'lit/directives/repeat.js';\nimport type { CollectionNameCacheInterface } from '@internetarchive/collection-name-cache';\nimport {\n FacetGroup,\n FacetBucket,\n SelectedFacets,\n getDefaultSelectedFacets,\n FacetEventDetails,\n} from '../models';\nimport { FacetRow } from './facet-row';\n\n@customElement('facets-template')\nexport class FacetsTemplate extends LitElement {\n @property({ type: Object }) facetGroup?: FacetGroup;\n\n @property({ type: Object }) selectedFacets?: SelectedFacets;\n\n @property({ type: String }) renderOn?: string;\n\n @property({ type: Object })\n collectionNameCache?: CollectionNameCacheInterface;\n\n private facetClicked(e: CustomEvent<FacetEventDetails>) {\n const { bucket, negative } = e.detail;\n if (bucket.state === 'none') {\n this.facetUnchecked(bucket);\n } else {\n this.facetChecked(bucket, negative);\n }\n\n this.dispatchFacetClickEvent(e.detail);\n }\n\n private facetChecked(bucket: FacetBucket, negative: boolean) {\n const { facetGroup, selectedFacets } = this;\n if (!facetGroup) return;\n\n let newFacets: SelectedFacets;\n if (selectedFacets) {\n newFacets = {\n ...selectedFacets,\n };\n } else {\n newFacets = getDefaultSelectedFacets();\n }\n newFacets[facetGroup.key][bucket.key] = {\n ...bucket,\n state: FacetRow.getFacetState(true, negative),\n };\n\n this.selectedFacets = newFacets;\n this.dispatchSelectedFacetsChanged();\n }\n\n private facetUnchecked(bucket: FacetBucket) {\n const { facetGroup, selectedFacets } = this;\n if (!facetGroup) return;\n\n let newFacets: SelectedFacets;\n if (selectedFacets) {\n newFacets = {\n ...selectedFacets,\n };\n } else {\n newFacets = getDefaultSelectedFacets();\n }\n delete newFacets[facetGroup.key][bucket.key];\n\n this.selectedFacets = newFacets;\n this.dispatchSelectedFacetsChanged();\n }\n\n private dispatchFacetClickEvent(detail: FacetEventDetails) {\n const event = new CustomEvent<FacetEventDetails>('facetClick', {\n detail,\n composed: true,\n });\n this.dispatchEvent(event);\n }\n\n private dispatchSelectedFacetsChanged() {\n const event = new CustomEvent<SelectedFacets>('selectedFacetsChanged', {\n detail: this.selectedFacets,\n bubbles: true,\n composed: true,\n });\n this.dispatchEvent(event);\n }\n\n private get facetsTemplate(): TemplateResult | typeof nothing {\n const { facetGroup } = this;\n if (!facetGroup) return nothing;\n\n let facetBuckets = facetGroup.buckets as FacetBucket[];\n\n /**\n * sorting FacetBucket before render page / modal\n * - first, selected items should be at top having sorted\n * - second, suppressed/hidden items should be after selected having sorted\n * - and then no-selected / not suppressed items should render having sorted\n */\n facetBuckets = [\n ...facetBuckets\n .filter(x => x.state === 'selected')\n .sort((a, b) => (a.count < b.count ? 1 : -1)),\n ...facetBuckets\n .filter(x => x.state === 'hidden')\n .sort((a, b) => (a.count < b.count ? 1 : -1)),\n ...facetBuckets.filter(x => x.state === 'none'),\n ];\n\n return html`\n <div class=\"facets-on-${this.renderOn}\">\n ${repeat(\n facetBuckets,\n bucket => `${facetGroup.key}:${bucket.key}`,\n bucket => html`<facet-row\n .facetType=${facetGroup.key}\n .bucket=${bucket}\n .collectionNameCache=${this.collectionNameCache}\n @facetClick=${this.facetClicked}\n ></facet-row>`\n )}\n </div>\n `;\n }\n\n render() {\n return html`${this.facetsTemplate}`;\n }\n\n static get styles(): CSSResultGroup {\n return css`\n @media (max-width: 560px) {\n .facets-on-modal {\n column-count: 1 !important;\n }\n }\n .facets-on-modal {\n column-gap: 15px;\n column-count: 3;\n }\n\n ul.facet-list {\n list-style: none;\n margin: 0;\n padding: 0;\n }\n ul.facet-list li {\n margin-bottom: 0.2rem;\n display: grid;\n }\n\n .facet-row {\n display: flex;\n font-weight: 500;\n font-size: 1.2rem;\n margin: 2.5px auto;\n height: auto;\n border-top: var(--facet-row-border-top, 1px solid transparent);\n border-bottom: var(--facet-row-border-bottom, 1px solid transparent);\n overflow: hidden;\n }\n\n a:link,\n a:visited {\n text-decoration: none;\n color: var(--ia-theme-link-color, #4b64ff);\n }\n a:hover {\n text-decoration: underline;\n }\n `;\n }\n}\n"]}
@@ -115,6 +115,9 @@ export declare type PrefixFilterCounts = Record<string, number>;
115
115
  * that are needed to fetch the filter counts from the backend.
116
116
  */
117
117
  export declare const prefixFilterAggregationKeys: Record<PrefixFilterType, string>;
118
+ /**
119
+ * Union of the facet types that are available in the sidebar.
120
+ */
118
121
  export declare type FacetOption = 'subject' | 'lending' | 'mediatype' | 'language' | 'creator' | 'collection' | 'year';
119
122
  export declare type SelectedFacetState = 'selected' | 'hidden';
120
123
  export declare type FacetState = SelectedFacetState | 'none';
@@ -129,9 +132,22 @@ export interface FacetGroup {
129
132
  key: FacetOption;
130
133
  buckets: FacetBucket[];
131
134
  }
135
+ /**
136
+ * Information about a user interaction event on a facet.
137
+ */
132
138
  export declare type FacetEventDetails = {
133
- key: FacetOption;
134
- state: FacetState;
139
+ /**
140
+ * The type of facet that was interacted with (e.g., 'mediatype', 'language', ...).
141
+ */
142
+ facetType: FacetOption;
143
+ /**
144
+ * The bucket corresponding to the facet that was interacted with, including the
145
+ * updated state of the facet after the interaction.
146
+ */
147
+ bucket: FacetBucket;
148
+ /**
149
+ * Whether the interaction occurred on a negative facet.
150
+ */
135
151
  negative: boolean;
136
152
  };
137
153
  export declare type FacetValue = string;
@@ -1 +1 @@
1
- {"version":3,"file":"models.js","sourceRoot":"","sources":["../../src/models.ts"],"names":[],"mappings":"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","sourcesContent":["import type { MediaType } from '@internetarchive/field-parsers';\nimport {\n AggregationSortType,\n SortDirection,\n} from '@internetarchive/search-service';\n\nexport interface TileModel {\n averageRating?: number;\n checked: boolean; // Whether this tile is currently checked for item management functions\n collectionIdentifier?: string;\n collectionName?: string;\n collectionFilesCount: number;\n collections: string[];\n collectionSize: number;\n commentCount: number;\n creator?: string;\n creators: string[];\n dateAdded?: Date; // Date added to public search (software-defined) [from: addeddate]\n dateArchived?: Date; // Date archived (software-defined) item created on archive.org [from: publicdate]\n datePublished?: Date; // Date work published in the world (user-defined) [from: date]\n dateReviewed?: Date; // Date reviewed (user-created) most recent review [from: reviewdate]\n description?: string;\n favCount: number;\n href?: string;\n identifier: string;\n issue?: string;\n itemCount: number;\n mediatype: MediaType;\n source?: string;\n snippets?: string[];\n subjects: string[];\n title: string;\n viewCount: number;\n volume?: string;\n weeklyViewCount?: number;\n loginRequired: boolean;\n contentWarning: boolean;\n}\n\nexport type CollectionDisplayMode = 'grid' | 'list-compact' | 'list-detail';\n\nexport type TileDisplayMode =\n | 'grid'\n | 'list-compact'\n | 'list-detail'\n | 'list-header';\n\n/**\n * This is mainly used to set the cookies for the collection display mode.\n *\n * It allows the user to set different modes for different contexts (collection page, search page, etc).\n */\nexport type CollectionBrowserContext = 'collection' | 'search';\n\n/**\n * The sort fields shown in the sort filter bar\n */\nexport enum SortField {\n 'default' = 'default',\n 'unrecognized' = 'unrecognized',\n 'relevance' = 'relevance',\n 'alltimeview' = 'alltimeview',\n 'weeklyview' = 'weeklyview',\n 'title' = 'title',\n 'date' = 'date',\n 'datearchived' = 'datearchived',\n 'datereviewed' = 'datereviewed',\n 'dateadded' = 'dateadded',\n 'datefavorited' = 'datefavorited',\n 'creator' = 'creator',\n}\n\nexport interface SortOption {\n /**\n * The SortField enum member corresponding to this option.\n */\n field: SortField;\n\n /**\n * The default sort direction to apply when this sort option is first selected.\n */\n defaultSortDirection: SortDirection | null;\n\n /**\n * Whether this sort option allows its sort direction to be changed from the default.\n */\n canSetDirection: boolean;\n\n /**\n * Whether this sort option may appear in the sort bar.\n */\n shownInSortBar: boolean;\n\n /**\n * Whether this sort option should be saved to the URL.\n * If false, then no `sort` param will be added to the URL when this sort option\n * is selected.\n */\n shownInURL: boolean;\n\n /**\n * Whether this sort option is passed to the search service.\n * If false, then no sort param will be passed to the search service at all when\n * this sort option is selected.\n */\n handledBySearchService: boolean;\n\n /**\n * The string identifying this sort field to the search service & backend API.\n */\n searchServiceKey?: string;\n\n /**\n * The human-readable name to use for this option in the sort bar (if applicable).\n */\n displayName: string;\n\n /**\n * A list of URL param keys that should be mapped to this sort option.\n * E.g., both `title` and `titleSorter` in the URL map to the `SortField.title` option.\n */\n urlNames: (string | null | undefined)[];\n}\n\nexport const SORT_OPTIONS: Record<SortField, SortOption> = {\n // Default sort is the case where the user has not specified a sort option via the sort bar or URL.\n // In these cases, we defer to whatever sort the backend chooses.\n // For the search page, the default is always relevance sort.\n // For collection pages _without a query_, the default is usually weekly views, but this can be\n // overridden by the collection's `sort-by` metadata entry. If a query _is_ specified, then the\n // default is again relevance sort.\n // For fav-* collections only, the default is instead sorting by date favorited.\n [SortField.default]: {\n field: SortField.default,\n defaultSortDirection: null,\n canSetDirection: false,\n shownInSortBar: false,\n shownInURL: false,\n handledBySearchService: false, // We rely on the PPS default sort handling in these cases\n displayName: '',\n urlNames: ['', null, undefined], // Empty or nullish sort params result in default sorting\n },\n // Unrecognized sort is the case where the user has specified a sort in the URL, but it is not\n // one of the options listed in this map. We still want these unrecognized sorts to be applied\n // when searching, but they are not displayed in the sort bar and we do not actively manage\n // their URL param beyond flipping the direction as needed.\n [SortField.unrecognized]: {\n field: SortField.unrecognized,\n defaultSortDirection: null,\n canSetDirection: true,\n shownInSortBar: false,\n shownInURL: false,\n handledBySearchService: true, // The unrecognized sort param is passed along as-is\n displayName: '',\n urlNames: [],\n },\n // Relevance sort is unique in that it does not produce a URL param when it is set.\n // It is only available when there is a user-specified query that relevancy can be scored against.\n // Therefore, it does not appear as a sort bar option when browsing a collection with no query set.\n [SortField.relevance]: {\n field: SortField.relevance,\n defaultSortDirection: null,\n canSetDirection: false,\n shownInSortBar: true,\n shownInURL: false,\n handledBySearchService: false,\n displayName: 'Relevance',\n urlNames: ['_score'],\n },\n [SortField.alltimeview]: {\n field: SortField.alltimeview,\n defaultSortDirection: 'desc',\n canSetDirection: true,\n shownInSortBar: true,\n shownInURL: true,\n handledBySearchService: true,\n searchServiceKey: 'downloads',\n displayName: 'All-time views',\n urlNames: ['downloads'],\n },\n [SortField.weeklyview]: {\n field: SortField.weeklyview,\n defaultSortDirection: 'desc',\n canSetDirection: true,\n shownInSortBar: true,\n shownInURL: true,\n handledBySearchService: true,\n searchServiceKey: 'week',\n displayName: 'Weekly views',\n urlNames: ['week'],\n },\n [SortField.title]: {\n field: SortField.title,\n defaultSortDirection: 'asc',\n canSetDirection: true,\n shownInSortBar: true,\n shownInURL: true,\n handledBySearchService: true,\n searchServiceKey: 'titleSorter',\n displayName: 'Title',\n urlNames: ['title', 'titleSorter'],\n },\n [SortField.date]: {\n field: SortField.date,\n defaultSortDirection: 'desc',\n canSetDirection: true,\n shownInSortBar: true,\n shownInURL: true,\n handledBySearchService: true,\n searchServiceKey: 'date',\n displayName: 'Date published',\n urlNames: ['date'],\n },\n [SortField.datearchived]: {\n field: SortField.datearchived,\n defaultSortDirection: 'desc',\n canSetDirection: true,\n shownInSortBar: true,\n shownInURL: true,\n handledBySearchService: true,\n searchServiceKey: 'publicdate',\n displayName: 'Date archived',\n urlNames: ['publicdate'],\n },\n [SortField.datereviewed]: {\n field: SortField.datereviewed,\n defaultSortDirection: 'desc',\n canSetDirection: true,\n shownInSortBar: true,\n shownInURL: true,\n handledBySearchService: true,\n searchServiceKey: 'reviewdate',\n displayName: 'Date reviewed',\n urlNames: ['reviewdate'],\n },\n [SortField.dateadded]: {\n field: SortField.dateadded,\n defaultSortDirection: 'desc',\n canSetDirection: true,\n shownInSortBar: true,\n shownInURL: true,\n handledBySearchService: true,\n searchServiceKey: 'addeddate',\n displayName: 'Date added',\n urlNames: ['addeddate'],\n },\n [SortField.datefavorited]: {\n field: SortField.datefavorited,\n defaultSortDirection: 'desc',\n canSetDirection: false,\n shownInSortBar: true, // But only when viewing fav-* collections\n shownInURL: false,\n handledBySearchService: false,\n searchServiceKey: 'favoritedate',\n displayName: 'Date favorited',\n urlNames: ['favoritedate'],\n },\n [SortField.creator]: {\n field: SortField.creator,\n defaultSortDirection: 'asc',\n canSetDirection: true,\n shownInSortBar: true,\n shownInURL: true,\n handledBySearchService: true,\n searchServiceKey: 'creatorSorter',\n displayName: 'Creator',\n urlNames: ['creator', 'creatorSorter'],\n },\n};\n\n/**\n * Returns the SortOption corresponding to the given API sort name, or\n * the \"unrecognized\" SortOption if none matches.\n */\nexport function sortOptionFromAPIString(sortName?: string | null): SortOption {\n return (\n Object.values(SORT_OPTIONS).find(opt =>\n opt.urlNames.some(name => sortName === name)\n ) ?? SORT_OPTIONS[SortField.unrecognized]\n );\n}\n\n/** A union of the fields that permit prefix filtering (e.g., alphabetical filtering) */\nexport type PrefixFilterType = 'title' | 'creator';\n\n/** A map from prefixes (e.g., initial letters) to the number of items matching that prefix */\nexport type PrefixFilterCounts = Record<string, number>;\n\n/**\n * A map from prefix filter types to the corresponding aggregation keys\n * that are needed to fetch the filter counts from the backend.\n */\nexport const prefixFilterAggregationKeys: Record<PrefixFilterType, string> = {\n title: 'firstTitle',\n creator: 'firstCreator',\n};\n\nexport type FacetOption =\n | 'subject'\n | 'lending'\n | 'mediatype'\n | 'language'\n | 'creator'\n | 'collection'\n | 'year';\n\nexport type SelectedFacetState = 'selected' | 'hidden';\n\nexport type FacetState = SelectedFacetState | 'none';\n\nexport interface FacetBucket {\n // for some facets, we augment the key with a display value\n displayText?: string;\n key: string;\n count: number;\n state: FacetState;\n}\n\nexport interface FacetGroup {\n title: string;\n key: FacetOption;\n buckets: FacetBucket[];\n}\n\nexport type FacetEventDetails = {\n key: FacetOption;\n state: FacetState;\n negative: boolean;\n};\n\nexport type FacetValue = string;\n\nexport type SelectedFacets = Record<\n FacetOption,\n Record<FacetValue, FacetBucket>\n>;\n\nexport const getDefaultSelectedFacets = (): SelectedFacets => ({\n subject: {},\n lending: {},\n mediatype: {},\n language: {},\n creator: {},\n collection: {},\n year: {},\n});\n\nexport const facetDisplayOrder: FacetOption[] = [\n 'mediatype',\n 'lending',\n 'year',\n 'subject',\n 'collection',\n 'creator',\n 'language',\n];\n\nexport const facetTitles: Record<FacetOption, string> = {\n subject: 'Subject',\n lending: 'Availability',\n mediatype: 'Media Type',\n language: 'Language',\n creator: 'Creator',\n collection: 'Collection',\n year: 'Year',\n};\n\n/**\n * The default sort type to use for each facet type\n */\nexport const defaultFacetSort: Record<FacetOption, AggregationSortType> = {\n subject: AggregationSortType.COUNT,\n lending: AggregationSortType.COUNT,\n mediatype: AggregationSortType.COUNT,\n language: AggregationSortType.COUNT,\n creator: AggregationSortType.COUNT,\n collection: AggregationSortType.COUNT,\n year: AggregationSortType.NUMERIC,\n};\n\n/**\n * The sort type corresponding to facet bucket values, for each facet type\n * (i.e., the opposite of \"sort by count\" for that type).\n */\nexport const valueFacetSort: Record<FacetOption, AggregationSortType> = {\n subject: AggregationSortType.ALPHABETICAL,\n lending: AggregationSortType.ALPHABETICAL,\n mediatype: AggregationSortType.ALPHABETICAL,\n language: AggregationSortType.ALPHABETICAL,\n creator: AggregationSortType.ALPHABETICAL,\n collection: AggregationSortType.ALPHABETICAL,\n year: AggregationSortType.NUMERIC,\n};\n\nexport type LendingFacetKey =\n | 'is_lendable'\n | 'is_borrowable'\n | 'available_to_borrow'\n | 'is_browsable'\n | 'available_to_browse'\n | 'is_readable'\n | 'available_to_waitlist';\n\n/**\n * Maps valid lending keys to whether they should be visible in the facet sidebar\n */\nexport const lendingFacetKeysVisibility: Record<LendingFacetKey, boolean> = {\n is_lendable: true,\n is_borrowable: false,\n available_to_borrow: true,\n is_browsable: false,\n available_to_browse: false,\n is_readable: true,\n available_to_waitlist: false,\n};\n\n/**\n * Maps valid, visible lending keys to their facet sidebar display text\n */\nexport const lendingFacetDisplayNames: Partial<\n Record<LendingFacetKey, string>\n> = {\n is_lendable: 'Lending Library',\n available_to_borrow: 'Borrow 14 Days',\n is_readable: 'Always Available',\n};\n\n/**\n * A record of which admin-only collections should be suppressed from being displayed\n * as facets or in an item's list of collections.\n */\nexport const suppressedCollections: Record<string, boolean> = {\n deemphasize: true,\n community: true,\n stream_only: true,\n samples_only: true,\n test_collection: true,\n printdisabled: true,\n 'openlibrary-ol': true,\n nationalemergencylibrary: true,\n china: true,\n americana: true,\n toronto: true,\n};\n"]}
1
+ {"version":3,"file":"models.js","sourceRoot":"","sources":["../../src/models.ts"],"names":[],"mappings":"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","sourcesContent":["import type { MediaType } from '@internetarchive/field-parsers';\nimport {\n AggregationSortType,\n SortDirection,\n} from '@internetarchive/search-service';\n\nexport interface TileModel {\n averageRating?: number;\n checked: boolean; // Whether this tile is currently checked for item management functions\n collectionIdentifier?: string;\n collectionName?: string;\n collectionFilesCount: number;\n collections: string[];\n collectionSize: number;\n commentCount: number;\n creator?: string;\n creators: string[];\n dateAdded?: Date; // Date added to public search (software-defined) [from: addeddate]\n dateArchived?: Date; // Date archived (software-defined) item created on archive.org [from: publicdate]\n datePublished?: Date; // Date work published in the world (user-defined) [from: date]\n dateReviewed?: Date; // Date reviewed (user-created) most recent review [from: reviewdate]\n description?: string;\n favCount: number;\n href?: string;\n identifier: string;\n issue?: string;\n itemCount: number;\n mediatype: MediaType;\n source?: string;\n snippets?: string[];\n subjects: string[];\n title: string;\n viewCount: number;\n volume?: string;\n weeklyViewCount?: number;\n loginRequired: boolean;\n contentWarning: boolean;\n}\n\nexport type CollectionDisplayMode = 'grid' | 'list-compact' | 'list-detail';\n\nexport type TileDisplayMode =\n | 'grid'\n | 'list-compact'\n | 'list-detail'\n | 'list-header';\n\n/**\n * This is mainly used to set the cookies for the collection display mode.\n *\n * It allows the user to set different modes for different contexts (collection page, search page, etc).\n */\nexport type CollectionBrowserContext = 'collection' | 'search';\n\n/**\n * The sort fields shown in the sort filter bar\n */\nexport enum SortField {\n 'default' = 'default',\n 'unrecognized' = 'unrecognized',\n 'relevance' = 'relevance',\n 'alltimeview' = 'alltimeview',\n 'weeklyview' = 'weeklyview',\n 'title' = 'title',\n 'date' = 'date',\n 'datearchived' = 'datearchived',\n 'datereviewed' = 'datereviewed',\n 'dateadded' = 'dateadded',\n 'datefavorited' = 'datefavorited',\n 'creator' = 'creator',\n}\n\nexport interface SortOption {\n /**\n * The SortField enum member corresponding to this option.\n */\n field: SortField;\n\n /**\n * The default sort direction to apply when this sort option is first selected.\n */\n defaultSortDirection: SortDirection | null;\n\n /**\n * Whether this sort option allows its sort direction to be changed from the default.\n */\n canSetDirection: boolean;\n\n /**\n * Whether this sort option may appear in the sort bar.\n */\n shownInSortBar: boolean;\n\n /**\n * Whether this sort option should be saved to the URL.\n * If false, then no `sort` param will be added to the URL when this sort option\n * is selected.\n */\n shownInURL: boolean;\n\n /**\n * Whether this sort option is passed to the search service.\n * If false, then no sort param will be passed to the search service at all when\n * this sort option is selected.\n */\n handledBySearchService: boolean;\n\n /**\n * The string identifying this sort field to the search service & backend API.\n */\n searchServiceKey?: string;\n\n /**\n * The human-readable name to use for this option in the sort bar (if applicable).\n */\n displayName: string;\n\n /**\n * A list of URL param keys that should be mapped to this sort option.\n * E.g., both `title` and `titleSorter` in the URL map to the `SortField.title` option.\n */\n urlNames: (string | null | undefined)[];\n}\n\nexport const SORT_OPTIONS: Record<SortField, SortOption> = {\n // Default sort is the case where the user has not specified a sort option via the sort bar or URL.\n // In these cases, we defer to whatever sort the backend chooses.\n // For the search page, the default is always relevance sort.\n // For collection pages _without a query_, the default is usually weekly views, but this can be\n // overridden by the collection's `sort-by` metadata entry. If a query _is_ specified, then the\n // default is again relevance sort.\n // For fav-* collections only, the default is instead sorting by date favorited.\n [SortField.default]: {\n field: SortField.default,\n defaultSortDirection: null,\n canSetDirection: false,\n shownInSortBar: false,\n shownInURL: false,\n handledBySearchService: false, // We rely on the PPS default sort handling in these cases\n displayName: '',\n urlNames: ['', null, undefined], // Empty or nullish sort params result in default sorting\n },\n // Unrecognized sort is the case where the user has specified a sort in the URL, but it is not\n // one of the options listed in this map. We still want these unrecognized sorts to be applied\n // when searching, but they are not displayed in the sort bar and we do not actively manage\n // their URL param beyond flipping the direction as needed.\n [SortField.unrecognized]: {\n field: SortField.unrecognized,\n defaultSortDirection: null,\n canSetDirection: true,\n shownInSortBar: false,\n shownInURL: false,\n handledBySearchService: true, // The unrecognized sort param is passed along as-is\n displayName: '',\n urlNames: [],\n },\n // Relevance sort is unique in that it does not produce a URL param when it is set.\n // It is only available when there is a user-specified query that relevancy can be scored against.\n // Therefore, it does not appear as a sort bar option when browsing a collection with no query set.\n [SortField.relevance]: {\n field: SortField.relevance,\n defaultSortDirection: null,\n canSetDirection: false,\n shownInSortBar: true,\n shownInURL: false,\n handledBySearchService: false,\n displayName: 'Relevance',\n urlNames: ['_score'],\n },\n [SortField.alltimeview]: {\n field: SortField.alltimeview,\n defaultSortDirection: 'desc',\n canSetDirection: true,\n shownInSortBar: true,\n shownInURL: true,\n handledBySearchService: true,\n searchServiceKey: 'downloads',\n displayName: 'All-time views',\n urlNames: ['downloads'],\n },\n [SortField.weeklyview]: {\n field: SortField.weeklyview,\n defaultSortDirection: 'desc',\n canSetDirection: true,\n shownInSortBar: true,\n shownInURL: true,\n handledBySearchService: true,\n searchServiceKey: 'week',\n displayName: 'Weekly views',\n urlNames: ['week'],\n },\n [SortField.title]: {\n field: SortField.title,\n defaultSortDirection: 'asc',\n canSetDirection: true,\n shownInSortBar: true,\n shownInURL: true,\n handledBySearchService: true,\n searchServiceKey: 'titleSorter',\n displayName: 'Title',\n urlNames: ['title', 'titleSorter'],\n },\n [SortField.date]: {\n field: SortField.date,\n defaultSortDirection: 'desc',\n canSetDirection: true,\n shownInSortBar: true,\n shownInURL: true,\n handledBySearchService: true,\n searchServiceKey: 'date',\n displayName: 'Date published',\n urlNames: ['date'],\n },\n [SortField.datearchived]: {\n field: SortField.datearchived,\n defaultSortDirection: 'desc',\n canSetDirection: true,\n shownInSortBar: true,\n shownInURL: true,\n handledBySearchService: true,\n searchServiceKey: 'publicdate',\n displayName: 'Date archived',\n urlNames: ['publicdate'],\n },\n [SortField.datereviewed]: {\n field: SortField.datereviewed,\n defaultSortDirection: 'desc',\n canSetDirection: true,\n shownInSortBar: true,\n shownInURL: true,\n handledBySearchService: true,\n searchServiceKey: 'reviewdate',\n displayName: 'Date reviewed',\n urlNames: ['reviewdate'],\n },\n [SortField.dateadded]: {\n field: SortField.dateadded,\n defaultSortDirection: 'desc',\n canSetDirection: true,\n shownInSortBar: true,\n shownInURL: true,\n handledBySearchService: true,\n searchServiceKey: 'addeddate',\n displayName: 'Date added',\n urlNames: ['addeddate'],\n },\n [SortField.datefavorited]: {\n field: SortField.datefavorited,\n defaultSortDirection: 'desc',\n canSetDirection: false,\n shownInSortBar: true, // But only when viewing fav-* collections\n shownInURL: false,\n handledBySearchService: false,\n searchServiceKey: 'favoritedate',\n displayName: 'Date favorited',\n urlNames: ['favoritedate'],\n },\n [SortField.creator]: {\n field: SortField.creator,\n defaultSortDirection: 'asc',\n canSetDirection: true,\n shownInSortBar: true,\n shownInURL: true,\n handledBySearchService: true,\n searchServiceKey: 'creatorSorter',\n displayName: 'Creator',\n urlNames: ['creator', 'creatorSorter'],\n },\n};\n\n/**\n * Returns the SortOption corresponding to the given API sort name, or\n * the \"unrecognized\" SortOption if none matches.\n */\nexport function sortOptionFromAPIString(sortName?: string | null): SortOption {\n return (\n Object.values(SORT_OPTIONS).find(opt =>\n opt.urlNames.some(name => sortName === name)\n ) ?? SORT_OPTIONS[SortField.unrecognized]\n );\n}\n\n/** A union of the fields that permit prefix filtering (e.g., alphabetical filtering) */\nexport type PrefixFilterType = 'title' | 'creator';\n\n/** A map from prefixes (e.g., initial letters) to the number of items matching that prefix */\nexport type PrefixFilterCounts = Record<string, number>;\n\n/**\n * A map from prefix filter types to the corresponding aggregation keys\n * that are needed to fetch the filter counts from the backend.\n */\nexport const prefixFilterAggregationKeys: Record<PrefixFilterType, string> = {\n title: 'firstTitle',\n creator: 'firstCreator',\n};\n\n/**\n * Union of the facet types that are available in the sidebar.\n */\nexport type FacetOption =\n | 'subject'\n | 'lending'\n | 'mediatype'\n | 'language'\n | 'creator'\n | 'collection'\n | 'year';\n\nexport type SelectedFacetState = 'selected' | 'hidden';\n\nexport type FacetState = SelectedFacetState | 'none';\n\nexport interface FacetBucket {\n // for some facets, we augment the key with a display value\n displayText?: string;\n key: string;\n count: number;\n state: FacetState;\n}\n\nexport interface FacetGroup {\n title: string;\n key: FacetOption;\n buckets: FacetBucket[];\n}\n\n/**\n * Information about a user interaction event on a facet.\n */\nexport type FacetEventDetails = {\n /**\n * The type of facet that was interacted with (e.g., 'mediatype', 'language', ...).\n */\n facetType: FacetOption;\n /**\n * The bucket corresponding to the facet that was interacted with, including the\n * updated state of the facet after the interaction.\n */\n bucket: FacetBucket;\n /**\n * Whether the interaction occurred on a negative facet.\n */\n negative: boolean;\n};\n\nexport type FacetValue = string;\n\nexport type SelectedFacets = Record<\n FacetOption,\n Record<FacetValue, FacetBucket>\n>;\n\nexport const getDefaultSelectedFacets = (): SelectedFacets => ({\n subject: {},\n lending: {},\n mediatype: {},\n language: {},\n creator: {},\n collection: {},\n year: {},\n});\n\nexport const facetDisplayOrder: FacetOption[] = [\n 'mediatype',\n 'lending',\n 'year',\n 'subject',\n 'collection',\n 'creator',\n 'language',\n];\n\nexport const facetTitles: Record<FacetOption, string> = {\n subject: 'Subject',\n lending: 'Availability',\n mediatype: 'Media Type',\n language: 'Language',\n creator: 'Creator',\n collection: 'Collection',\n year: 'Year',\n};\n\n/**\n * The default sort type to use for each facet type\n */\nexport const defaultFacetSort: Record<FacetOption, AggregationSortType> = {\n subject: AggregationSortType.COUNT,\n lending: AggregationSortType.COUNT,\n mediatype: AggregationSortType.COUNT,\n language: AggregationSortType.COUNT,\n creator: AggregationSortType.COUNT,\n collection: AggregationSortType.COUNT,\n year: AggregationSortType.NUMERIC,\n};\n\n/**\n * The sort type corresponding to facet bucket values, for each facet type\n * (i.e., the opposite of \"sort by count\" for that type).\n */\nexport const valueFacetSort: Record<FacetOption, AggregationSortType> = {\n subject: AggregationSortType.ALPHABETICAL,\n lending: AggregationSortType.ALPHABETICAL,\n mediatype: AggregationSortType.ALPHABETICAL,\n language: AggregationSortType.ALPHABETICAL,\n creator: AggregationSortType.ALPHABETICAL,\n collection: AggregationSortType.ALPHABETICAL,\n year: AggregationSortType.NUMERIC,\n};\n\nexport type LendingFacetKey =\n | 'is_lendable'\n | 'is_borrowable'\n | 'available_to_borrow'\n | 'is_browsable'\n | 'available_to_browse'\n | 'is_readable'\n | 'available_to_waitlist';\n\n/**\n * Maps valid lending keys to whether they should be visible in the facet sidebar\n */\nexport const lendingFacetKeysVisibility: Record<LendingFacetKey, boolean> = {\n is_lendable: true,\n is_borrowable: false,\n available_to_borrow: true,\n is_browsable: false,\n available_to_browse: false,\n is_readable: true,\n available_to_waitlist: false,\n};\n\n/**\n * Maps valid, visible lending keys to their facet sidebar display text\n */\nexport const lendingFacetDisplayNames: Partial<\n Record<LendingFacetKey, string>\n> = {\n is_lendable: 'Lending Library',\n available_to_borrow: 'Borrow 14 Days',\n is_readable: 'Always Available',\n};\n\n/**\n * A record of which admin-only collections should be suppressed from being displayed\n * as facets or in an item's list of collections.\n */\nexport const suppressedCollections: Record<string, boolean> = {\n deemphasize: true,\n community: true,\n stream_only: true,\n samples_only: true,\n test_collection: true,\n printdisabled: true,\n 'openlibrary-ol': true,\n nationalemergencylibrary: true,\n china: true,\n americana: true,\n toronto: true,\n};\n"]}
@@ -127,13 +127,29 @@ describe('Collection Browser', () => {
127
127
  el.selectedFacets = mockedSelectedFacets;
128
128
  await el.updateComplete;
129
129
  el.facetClickHandler(new CustomEvent('facetClick', {
130
- detail: { key: 'mediatype', state: 'selected', negative: false },
130
+ detail: {
131
+ facetType: 'mediatype',
132
+ bucket: {
133
+ key: '',
134
+ state: 'selected',
135
+ count: 123,
136
+ },
137
+ negative: false,
138
+ },
131
139
  }));
132
140
  expect(mockAnalyticsHandler.callCategory).to.equal('search-service');
133
141
  expect(mockAnalyticsHandler.callAction).to.equal('facetSelected');
134
142
  expect(mockAnalyticsHandler.callLabel).to.equal('mediatype');
135
143
  el.facetClickHandler(new CustomEvent('facetClick', {
136
- detail: { key: 'mediatype', state: 'none', negative: false },
144
+ detail: {
145
+ facetType: 'mediatype',
146
+ bucket: {
147
+ key: '',
148
+ state: 'none',
149
+ count: 123,
150
+ },
151
+ negative: false,
152
+ },
137
153
  }));
138
154
  expect(el.selectedFacets).to.equal(mockedSelectedFacets);
139
155
  expect(mockAnalyticsHandler.callCategory).to.equal('search-service');
@@ -158,13 +174,29 @@ describe('Collection Browser', () => {
158
174
  el.selectedFacets = mockedSelectedFacets;
159
175
  await el.updateComplete;
160
176
  el.facetClickHandler(new CustomEvent('facetClick', {
161
- detail: { key: 'mediatype', state: 'hidden', negative: true },
177
+ detail: {
178
+ facetType: 'mediatype',
179
+ bucket: {
180
+ key: '',
181
+ state: 'hidden',
182
+ count: 123,
183
+ },
184
+ negative: true,
185
+ },
162
186
  }));
163
187
  expect(mockAnalyticsHandler.callCategory).to.equal('beta-search-service');
164
188
  expect(mockAnalyticsHandler.callAction).to.equal('facetNegativeSelected');
165
189
  expect(mockAnalyticsHandler.callLabel).to.equal('mediatype');
166
190
  el.facetClickHandler(new CustomEvent('facetClick', {
167
- detail: { key: 'mediatype', state: 'none', negative: true },
191
+ detail: {
192
+ facetType: 'mediatype',
193
+ bucket: {
194
+ key: '',
195
+ state: 'none',
196
+ count: 123,
197
+ },
198
+ negative: true,
199
+ },
168
200
  }));
169
201
  expect(el.selectedFacets).to.equal(mockedSelectedFacets);
170
202
  expect(mockAnalyticsHandler.callCategory).to.equal('beta-search-service');