@internetarchive/collection-browser 0.3.2-alpha.5 → 0.3.3-alpha.1
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.
- package/dist/src/collection-browser.js +9 -0
- package/dist/src/collection-browser.js.map +1 -1
- package/dist/src/restoration-state-handler.d.ts +2 -1
- package/dist/src/restoration-state-handler.js +10 -0
- package/dist/src/restoration-state-handler.js.map +1 -1
- package/dist/src/tiles/image-block.js +4 -8
- package/dist/src/tiles/image-block.js.map +1 -1
- package/dist/src/tiles/list/tile-list-compact.js +1 -0
- package/dist/src/tiles/list/tile-list-compact.js.map +1 -1
- package/dist/src/tiles/list/tile-list.js +1 -0
- package/dist/src/tiles/list/tile-list.js.map +1 -1
- package/dist/src/tiles/overlay/icon-overlay.js +3 -4
- package/dist/src/tiles/overlay/icon-overlay.js.map +1 -1
- package/dist/test/collection-browser.test.js +28 -2
- package/dist/test/collection-browser.test.js.map +1 -1
- package/dist/test/image-block.test.d.ts +1 -0
- package/dist/test/image-block.test.js +79 -0
- package/dist/test/image-block.test.js.map +1 -0
- package/dist/test/tiles/list/tile-list-compact.test.d.ts +1 -0
- package/dist/test/tiles/list/tile-list-compact.test.js +31 -0
- package/dist/test/tiles/list/tile-list-compact.test.js.map +1 -0
- package/package.json +1 -1
- package/src/collection-browser.ts +24 -0
- package/src/restoration-state-handler.ts +19 -1
- package/src/tiles/image-block.ts +7 -10
- package/src/tiles/list/tile-list-compact.ts +1 -0
- package/src/tiles/list/tile-list.ts +1 -0
- package/src/tiles/overlay/icon-overlay.ts +3 -4
- package/test/collection-browser.test.ts +31 -2
- package/test/image-block.test.ts +86 -0
- package/test/tiles/list/tile-list-compact.test.ts +38 -0
package/package.json
CHANGED
|
@@ -619,6 +619,14 @@ export class CollectionBrowser
|
|
|
619
619
|
}
|
|
620
620
|
|
|
621
621
|
updated(changed: PropertyValues) {
|
|
622
|
+
if (changed.has('baseQuery') || changed.has('searchType'))
|
|
623
|
+
console.log(
|
|
624
|
+
'updated',
|
|
625
|
+
changed,
|
|
626
|
+
changed.get('baseQuery'),
|
|
627
|
+
changed.get('searchType'),
|
|
628
|
+
this.baseImageUrl
|
|
629
|
+
);
|
|
622
630
|
if (
|
|
623
631
|
changed.has('displayMode') ||
|
|
624
632
|
changed.has('baseNavigationUrl') ||
|
|
@@ -760,6 +768,7 @@ export class CollectionBrowser
|
|
|
760
768
|
private previousQueryKey?: string;
|
|
761
769
|
|
|
762
770
|
private async handleQueryChange() {
|
|
771
|
+
console.log('handleQueryChange', this.baseQuery);
|
|
763
772
|
// only reset if the query has actually changed
|
|
764
773
|
if (!this.searchService || this.pageFetchQueryKey === this.previousQueryKey)
|
|
765
774
|
return;
|
|
@@ -775,6 +784,7 @@ export class CollectionBrowser
|
|
|
775
784
|
this.initialQueryChangeHappened = true;
|
|
776
785
|
// if the query changed as part of a window.history pop event, we don't want to
|
|
777
786
|
// persist the state because it overwrites the forward history
|
|
787
|
+
|
|
778
788
|
if (!this.historyPopOccurred) {
|
|
779
789
|
this.persistState();
|
|
780
790
|
this.historyPopOccurred = false;
|
|
@@ -809,7 +819,14 @@ export class CollectionBrowser
|
|
|
809
819
|
|
|
810
820
|
private restoreState() {
|
|
811
821
|
const restorationState = this.restorationStateHandler.getRestorationState();
|
|
822
|
+
console.log(
|
|
823
|
+
'restoring state',
|
|
824
|
+
restorationState.baseQuery,
|
|
825
|
+
restorationState.searchType
|
|
826
|
+
);
|
|
812
827
|
this.displayMode = restorationState.displayMode;
|
|
828
|
+
if (restorationState.searchType != null)
|
|
829
|
+
this.searchType = restorationState.searchType;
|
|
813
830
|
this.selectedSort = restorationState.selectedSort ?? SortField.relevance;
|
|
814
831
|
this.sortDirection = restorationState.sortDirection ?? null;
|
|
815
832
|
this.selectedTitleFilter = restorationState.selectedTitleFilter ?? null;
|
|
@@ -831,6 +848,7 @@ export class CollectionBrowser
|
|
|
831
848
|
private persistState() {
|
|
832
849
|
const restorationState: RestorationState = {
|
|
833
850
|
displayMode: this.displayMode,
|
|
851
|
+
searchType: this.searchType,
|
|
834
852
|
sortParam: this.sortParam ?? undefined,
|
|
835
853
|
selectedSort: this.selectedSort,
|
|
836
854
|
sortDirection: this.sortDirection ?? undefined,
|
|
@@ -845,6 +863,11 @@ export class CollectionBrowser
|
|
|
845
863
|
selectedTitleFilter: this.selectedTitleFilter ?? undefined,
|
|
846
864
|
selectedCreatorFilter: this.selectedCreatorFilter ?? undefined,
|
|
847
865
|
};
|
|
866
|
+
console.log(
|
|
867
|
+
'persisting state',
|
|
868
|
+
restorationState.baseQuery,
|
|
869
|
+
restorationState.searchType
|
|
870
|
+
);
|
|
848
871
|
this.restorationStateHandler.persistState(restorationState);
|
|
849
872
|
}
|
|
850
873
|
|
|
@@ -1070,6 +1093,7 @@ export class CollectionBrowser
|
|
|
1070
1093
|
private pageFetchesInProgress: Record<string, Set<number>> = {};
|
|
1071
1094
|
|
|
1072
1095
|
async fetchPage(pageNumber: number) {
|
|
1096
|
+
console.log('fetchPage', this.baseQuery);
|
|
1073
1097
|
if (!this.fullQuery) return;
|
|
1074
1098
|
|
|
1075
1099
|
// if we already have data, don't fetch again
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
SearchType,
|
|
3
|
+
SortDirection,
|
|
4
|
+
SortParam,
|
|
5
|
+
} from '@internetarchive/search-service';
|
|
2
6
|
import { getCookie, setCookie } from 'typescript-cookie';
|
|
3
7
|
import {
|
|
4
8
|
MetadataFieldToSortField,
|
|
@@ -14,6 +18,7 @@ import {
|
|
|
14
18
|
|
|
15
19
|
export interface RestorationState {
|
|
16
20
|
displayMode?: CollectionDisplayMode;
|
|
21
|
+
searchType?: SearchType;
|
|
17
22
|
sortParam?: SortParam;
|
|
18
23
|
selectedSort?: SortField;
|
|
19
24
|
sortDirection?: SortDirection;
|
|
@@ -87,12 +92,20 @@ export class RestorationStateHandler
|
|
|
87
92
|
private persistQueryStateToUrl(state: RestorationState) {
|
|
88
93
|
const url = new URL(window.location.href);
|
|
89
94
|
const { searchParams } = url;
|
|
95
|
+
searchParams.delete('sin');
|
|
90
96
|
searchParams.delete('sort');
|
|
91
97
|
searchParams.delete('query');
|
|
92
98
|
searchParams.delete('page');
|
|
93
99
|
searchParams.delete('and[]');
|
|
94
100
|
searchParams.delete('not[]');
|
|
95
101
|
|
|
102
|
+
if (state.searchType) {
|
|
103
|
+
searchParams.set(
|
|
104
|
+
'sin',
|
|
105
|
+
state.searchType === SearchType.FULLTEXT ? 'TXT' : ''
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
96
109
|
if (state.sortParam) {
|
|
97
110
|
const prefix = state.sortParam.direction === 'desc' ? '-' : '';
|
|
98
111
|
searchParams.set('sort', `${prefix}${state.sortParam.field}`);
|
|
@@ -155,6 +168,7 @@ export class RestorationStateHandler
|
|
|
155
168
|
|
|
156
169
|
private loadQueryStateFromUrl(): RestorationState {
|
|
157
170
|
const url = new URL(window.location.href);
|
|
171
|
+
const searchInside = url.searchParams.get('sin');
|
|
158
172
|
const pageNumber = url.searchParams.get('page');
|
|
159
173
|
const searchQuery = url.searchParams.get('query');
|
|
160
174
|
const sortQuery = url.searchParams.get('sort');
|
|
@@ -173,6 +187,10 @@ export class RestorationStateHandler
|
|
|
173
187
|
},
|
|
174
188
|
};
|
|
175
189
|
|
|
190
|
+
if (searchInside) {
|
|
191
|
+
restorationState.searchType =
|
|
192
|
+
searchInside === 'TXT' ? SearchType.FULLTEXT : undefined; // No explicit metadata sin
|
|
193
|
+
}
|
|
176
194
|
if (pageNumber) {
|
|
177
195
|
const parsed = parseInt(pageNumber, 10);
|
|
178
196
|
restorationState.currentPage = parsed;
|
package/src/tiles/image-block.ts
CHANGED
|
@@ -22,9 +22,8 @@ export class ImageBlock extends LitElement {
|
|
|
22
22
|
@property({ type: String }) viewSize: string = 'desktop';
|
|
23
23
|
|
|
24
24
|
render() {
|
|
25
|
-
if (!this.model?.identifier)
|
|
26
|
-
|
|
27
|
-
}
|
|
25
|
+
if (!this.model?.identifier) return nothing;
|
|
26
|
+
|
|
28
27
|
return html`
|
|
29
28
|
<div class=${classMap(this.baseClass)}>
|
|
30
29
|
<item-image
|
|
@@ -52,9 +51,9 @@ export class ImageBlock extends LitElement {
|
|
|
52
51
|
private get iconOverlayTemplate() {
|
|
53
52
|
if (!this.isListTile) return nothing;
|
|
54
53
|
|
|
55
|
-
if (!this.model?.loginRequired && !this.model?.contentWarning)
|
|
54
|
+
if (!this.model?.loginRequired && !this.model?.contentWarning)
|
|
56
55
|
return nothing;
|
|
57
|
-
|
|
56
|
+
|
|
58
57
|
return html`
|
|
59
58
|
<icon-overlay
|
|
60
59
|
.loggedIn=${this.loggedIn}
|
|
@@ -65,13 +64,11 @@ export class ImageBlock extends LitElement {
|
|
|
65
64
|
}
|
|
66
65
|
|
|
67
66
|
private get textOverlayTemplate() {
|
|
68
|
-
if (this.isListTile)
|
|
69
|
-
return nothing;
|
|
70
|
-
}
|
|
67
|
+
if (this.isListTile) return nothing;
|
|
71
68
|
|
|
72
|
-
if (!this.model?.loginRequired && !this.model?.contentWarning)
|
|
69
|
+
if (!this.model?.loginRequired && !this.model?.contentWarning)
|
|
73
70
|
return nothing;
|
|
74
|
-
|
|
71
|
+
|
|
75
72
|
return html`
|
|
76
73
|
<text-overlay
|
|
77
74
|
.loggedIn=${this.loggedIn}
|
|
@@ -11,10 +11,9 @@ export class IconOverlay extends LitElement {
|
|
|
11
11
|
@property({ type: Boolean }) loginRequired = false;
|
|
12
12
|
|
|
13
13
|
render() {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
return html`${restrictedIcon}`;
|
|
14
|
+
return this.loginRequired && !this.loggedIn
|
|
15
|
+
? html`${loginRequiredIcon}`
|
|
16
|
+
: html`${restrictedIcon}`;
|
|
18
17
|
}
|
|
19
18
|
|
|
20
19
|
static get styles(): CSSResultGroup {
|
|
@@ -18,6 +18,21 @@ import { MockAnalyticsHandler } from './mocks/mock-analytics-handler';
|
|
|
18
18
|
import { analyticsCategories } from '../src/utils/analytics-events';
|
|
19
19
|
|
|
20
20
|
describe('Collection Browser', () => {
|
|
21
|
+
beforeEach(async () => {
|
|
22
|
+
// Apparently query params set by one test can bleed into other tests.
|
|
23
|
+
// Since collection browser restores its state from certain query params, we need
|
|
24
|
+
// to clear these before each test to ensure they run in isolation from one another.
|
|
25
|
+
const url = new URL(window.location.href);
|
|
26
|
+
const { searchParams } = url;
|
|
27
|
+
searchParams.delete('sin');
|
|
28
|
+
searchParams.delete('sort');
|
|
29
|
+
searchParams.delete('query');
|
|
30
|
+
searchParams.delete('page');
|
|
31
|
+
searchParams.delete('and[]');
|
|
32
|
+
searchParams.delete('not[]');
|
|
33
|
+
window.history.replaceState({}, '', url);
|
|
34
|
+
});
|
|
35
|
+
|
|
21
36
|
it('clear existing filter for facets & sort-bar', async () => {
|
|
22
37
|
const el = await fixture<CollectionBrowser>(
|
|
23
38
|
html`<collection-browser></collection-browser>`
|
|
@@ -370,8 +385,11 @@ describe('Collection Browser', () => {
|
|
|
370
385
|
});
|
|
371
386
|
|
|
372
387
|
it('sets sort properties when user changes sort', async () => {
|
|
388
|
+
const searchService = new MockSearchService();
|
|
373
389
|
const el = await fixture<CollectionBrowser>(
|
|
374
|
-
html`<collection-browser
|
|
390
|
+
html`<collection-browser
|
|
391
|
+
.searchService=${searchService}
|
|
392
|
+
></collection-browser>`
|
|
375
393
|
);
|
|
376
394
|
|
|
377
395
|
expect(el.selectedSort).to.equal(SortField.relevance);
|
|
@@ -397,10 +415,17 @@ describe('Collection Browser', () => {
|
|
|
397
415
|
});
|
|
398
416
|
|
|
399
417
|
it('scrolls to page', async () => {
|
|
418
|
+
const searchService = new MockSearchService();
|
|
400
419
|
const el = await fixture<CollectionBrowser>(
|
|
401
|
-
html`<collection-browser
|
|
420
|
+
html`<collection-browser
|
|
421
|
+
.searchService=${searchService}
|
|
422
|
+
></collection-browser>`
|
|
402
423
|
);
|
|
403
424
|
|
|
425
|
+
// Infinite scroller won't exist unless there's a base query
|
|
426
|
+
el.baseQuery = 'collection:foo';
|
|
427
|
+
await el.updateComplete;
|
|
428
|
+
|
|
404
429
|
const infiniteScroller = el.shadowRoot?.querySelector(
|
|
405
430
|
'infinite-scroller'
|
|
406
431
|
) as InfiniteScroller;
|
|
@@ -435,6 +460,10 @@ describe('Collection Browser', () => {
|
|
|
435
460
|
);
|
|
436
461
|
const infiniteScrollerRefreshSpy = sinon.spy();
|
|
437
462
|
|
|
463
|
+
// Infinite scroller won't exist unless there's a base query
|
|
464
|
+
el.baseQuery = 'collection:foo';
|
|
465
|
+
await el.updateComplete;
|
|
466
|
+
|
|
438
467
|
const infiniteScroller = el.shadowRoot?.querySelector('infinite-scroller');
|
|
439
468
|
(infiniteScroller as InfiniteScroller).reload = infiniteScrollerRefreshSpy;
|
|
440
469
|
expect(infiniteScrollerRefreshSpy.called).to.be.false;
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/* eslint-disable import/no-duplicates */
|
|
2
|
+
import { expect, fixture } from '@open-wc/testing';
|
|
3
|
+
import { html } from 'lit';
|
|
4
|
+
import type { ImageBlock } from '../src/tiles/image-block';
|
|
5
|
+
|
|
6
|
+
import '../src/tiles/image-block';
|
|
7
|
+
|
|
8
|
+
describe('Image block component', () => {
|
|
9
|
+
it('should render component grid display mode', async () => {
|
|
10
|
+
const el = await fixture<ImageBlock>(html`
|
|
11
|
+
<image-block
|
|
12
|
+
.model=${{
|
|
13
|
+
loggedInRequired: true,
|
|
14
|
+
contentWarning: true,
|
|
15
|
+
identifier: 'goody',
|
|
16
|
+
}}
|
|
17
|
+
.baseImageUrl=${'https://archive.org'}
|
|
18
|
+
.isCompactTile=${false}
|
|
19
|
+
.isListTile=${false}
|
|
20
|
+
.viewSize=${'grid'}
|
|
21
|
+
.loggedIn=${false}
|
|
22
|
+
>
|
|
23
|
+
</image-block>
|
|
24
|
+
`);
|
|
25
|
+
|
|
26
|
+
const viewSize = el.shadowRoot?.querySelector('.grid');
|
|
27
|
+
const itemImage = el.shadowRoot?.querySelector('item-image');
|
|
28
|
+
const textOverlay = el.shadowRoot?.querySelector('text-overlay');
|
|
29
|
+
|
|
30
|
+
expect(viewSize).to.exist;
|
|
31
|
+
expect(itemImage).to.exist;
|
|
32
|
+
expect(textOverlay).to.exist;
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('should render component list display mode', async () => {
|
|
36
|
+
const el = await fixture<ImageBlock>(html`
|
|
37
|
+
<image-block
|
|
38
|
+
.model=${{
|
|
39
|
+
loggedInRequired: true,
|
|
40
|
+
contentWarning: true,
|
|
41
|
+
identifier: 'goody',
|
|
42
|
+
}}
|
|
43
|
+
.baseImageUrl=${'https://archive.org'}
|
|
44
|
+
.isCompactTile=${false}
|
|
45
|
+
.isListTile=${true}
|
|
46
|
+
.viewSize=${'desktop'}
|
|
47
|
+
.loggedIn=${false}
|
|
48
|
+
>
|
|
49
|
+
</image-block>
|
|
50
|
+
`);
|
|
51
|
+
|
|
52
|
+
const viewSize = el.shadowRoot?.querySelector('.list.desktop');
|
|
53
|
+
const itemImage = el.shadowRoot?.querySelector('item-image');
|
|
54
|
+
const iconOverlay = el.shadowRoot?.querySelector('icon-overlay');
|
|
55
|
+
|
|
56
|
+
expect(viewSize).to.exist;
|
|
57
|
+
expect(itemImage).to.exist;
|
|
58
|
+
expect(iconOverlay).to.exist;
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('should render component compact display mode', async () => {
|
|
62
|
+
const el = await fixture<ImageBlock>(html`
|
|
63
|
+
<image-block
|
|
64
|
+
.model=${{
|
|
65
|
+
loggedInRequired: true,
|
|
66
|
+
contentWarning: true,
|
|
67
|
+
identifier: 'goody',
|
|
68
|
+
}}
|
|
69
|
+
.baseImageUrl=${'https://archive.org'}
|
|
70
|
+
.isCompactTile=${true}
|
|
71
|
+
.isListTile=${true}
|
|
72
|
+
.viewSize=${'desktop'}
|
|
73
|
+
.loggedIn=${false}
|
|
74
|
+
>
|
|
75
|
+
</image-block>
|
|
76
|
+
`);
|
|
77
|
+
|
|
78
|
+
const viewSize = el.shadowRoot?.querySelector('.list-compact.desktop');
|
|
79
|
+
const itemImage = el.shadowRoot?.querySelector('item-image');
|
|
80
|
+
const iconOverlay = el.shadowRoot?.querySelector('icon-overlay');
|
|
81
|
+
|
|
82
|
+
expect(viewSize).to.exist;
|
|
83
|
+
expect(itemImage).to.exist;
|
|
84
|
+
expect(iconOverlay).to.exist;
|
|
85
|
+
});
|
|
86
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/* eslint-disable import/no-duplicates */
|
|
2
|
+
import { expect, fixture } from '@open-wc/testing';
|
|
3
|
+
import { html } from 'lit';
|
|
4
|
+
import type { TileListCompact } from '../../../src/tiles/list/tile-list-compact';
|
|
5
|
+
|
|
6
|
+
import '../../../src/tiles/list/tile-list-compact';
|
|
7
|
+
|
|
8
|
+
describe('List Tile Compact', () => {
|
|
9
|
+
it('should render initial component', async () => {
|
|
10
|
+
const el = await fixture<TileListCompact>(
|
|
11
|
+
html`<tile-list-compact></tile-list-compact>`
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
const listContainer = el.shadowRoot?.querySelector('#list-line');
|
|
15
|
+
const itemTitle = el.shadowRoot?.querySelector('#title');
|
|
16
|
+
const imageBlock = el.shadowRoot?.querySelector('image-block');
|
|
17
|
+
const itemIcon = el.shadowRoot?.querySelector('#icon');
|
|
18
|
+
const itemViews = el.shadowRoot?.querySelector('#views');
|
|
19
|
+
|
|
20
|
+
expect(listContainer).to.exist;
|
|
21
|
+
expect(itemTitle).to.exist;
|
|
22
|
+
expect(imageBlock).to.exist;
|
|
23
|
+
expect(itemIcon).to.exist;
|
|
24
|
+
expect(itemViews).to.exist;
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('should render with creator element with title', async () => {
|
|
28
|
+
const el = await fixture<TileListCompact>(html`
|
|
29
|
+
<tile-list-compact
|
|
30
|
+
.model=${{ creators: ['someone'] }}
|
|
31
|
+
></tile-list-compact>
|
|
32
|
+
`);
|
|
33
|
+
|
|
34
|
+
const creator = el.shadowRoot?.querySelector('#creator');
|
|
35
|
+
|
|
36
|
+
expect(creator).to.exist;
|
|
37
|
+
});
|
|
38
|
+
});
|