@internetarchive/collection-browser 4.2.1-alpha-webdev8165.0 → 4.3.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.
- package/.claude/settings.local.json +11 -0
- package/dist/src/data-source/collection-browser-data-source.d.ts +7 -0
- package/dist/src/data-source/collection-browser-data-source.js +24 -8
- package/dist/src/data-source/collection-browser-data-source.js.map +1 -1
- package/dist/src/models.d.ts +6 -0
- package/dist/src/models.js +16 -7
- package/dist/src/models.js.map +1 -1
- package/dist/src/restoration-state-handler.js +3 -1
- package/dist/src/restoration-state-handler.js.map +1 -1
- package/dist/src/tiles/list/tile-list-compact-header.js +45 -45
- package/dist/src/tiles/list/tile-list-compact-header.js.map +1 -1
- package/dist/src/tiles/list/tile-list-compact.js +99 -99
- package/dist/src/tiles/list/tile-list-compact.js.map +1 -1
- package/dist/src/tiles/list/tile-list.js +300 -299
- package/dist/src/tiles/list/tile-list.js.map +1 -1
- package/dist/src/tiles/tile-display-value-provider.d.ts +5 -0
- package/dist/src/tiles/tile-display-value-provider.js +9 -0
- package/dist/src/tiles/tile-display-value-provider.js.map +1 -1
- package/dist/test/data-source/collection-browser-data-source.test.js +54 -2
- package/dist/test/data-source/collection-browser-data-source.test.js.map +1 -1
- package/dist/test/restoration-state-handler.test.js +0 -70
- package/dist/test/restoration-state-handler.test.js.map +1 -1
- package/dist/test/tiles/list/tile-list-compact-header.test.d.ts +1 -0
- package/dist/test/tiles/list/tile-list-compact-header.test.js +36 -0
- package/dist/test/tiles/list/tile-list-compact-header.test.js.map +1 -0
- package/dist/test/tiles/list/tile-list.test.js +147 -127
- package/dist/test/tiles/list/tile-list.test.js.map +1 -1
- package/package.json +1 -1
- package/src/data-source/collection-browser-data-source.ts +1465 -1445
- package/src/models.ts +23 -7
- package/src/restoration-state-handler.ts +5 -1
- package/src/tiles/list/tile-list-compact-header.ts +86 -86
- package/src/tiles/list/tile-list-compact.ts +239 -239
- package/src/tiles/list/tile-list.ts +700 -700
- package/src/tiles/tile-display-value-provider.ts +134 -124
- package/test/data-source/collection-browser-data-source.test.ts +193 -131
- package/test/restoration-state-handler.test.ts +0 -89
- package/test/tiles/list/tile-list-compact-header.test.ts +43 -0
- package/test/tiles/list/tile-list.test.ts +576 -552
|
@@ -1,124 +1,134 @@
|
|
|
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
|
-
*
|
|
96
|
-
*
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
+
const captureHref = `https://web.archive.org/web/${captureDateStr}/${encodeURIComponent(
|
|
128
|
+
url,
|
|
129
|
+
)}`;
|
|
130
|
+
const captureText = formatDate(date, 'long');
|
|
131
|
+
|
|
132
|
+
return html` <a href=${captureHref}> ${captureText} </a> `;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -1,131 +1,193 @@
|
|
|
1
|
-
import { expect, fixture } from '@open-wc/testing';
|
|
2
|
-
import { html } from 'lit';
|
|
3
|
-
import sinon from 'sinon';
|
|
4
|
-
import { ItemHit, SearchType } from '@internetarchive/search-service';
|
|
5
|
-
import { CollectionBrowserDataSource } from '../../src/data-source/collection-browser-data-source';
|
|
6
|
-
import { TileModel } from '../../src/models';
|
|
7
|
-
import type { CollectionBrowser } from '../../src/collection-browser';
|
|
8
|
-
import '../../src/collection-browser';
|
|
9
|
-
import { MockSearchService } from '../mocks/mock-search-service';
|
|
10
|
-
|
|
11
|
-
const dataPage: TileModel[] = [
|
|
12
|
-
new TileModel(
|
|
13
|
-
new ItemHit({
|
|
14
|
-
fields: {
|
|
15
|
-
identifier: 'foo',
|
|
16
|
-
},
|
|
17
|
-
}),
|
|
18
|
-
),
|
|
19
|
-
new TileModel(
|
|
20
|
-
new ItemHit({
|
|
21
|
-
fields: {
|
|
22
|
-
identifier: 'bar',
|
|
23
|
-
},
|
|
24
|
-
}),
|
|
25
|
-
),
|
|
26
|
-
];
|
|
27
|
-
|
|
28
|
-
describe('Collection Browser Data Source', () => {
|
|
29
|
-
let host: CollectionBrowser;
|
|
30
|
-
|
|
31
|
-
beforeEach(async () => {
|
|
32
|
-
host = await fixture<CollectionBrowser>(html`
|
|
33
|
-
<collection-browser></collection-browser>
|
|
34
|
-
`);
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it('can add and retrieve data pages', () => {
|
|
38
|
-
const dataSource = new CollectionBrowserDataSource(host);
|
|
39
|
-
dataSource.addPage(1, dataPage);
|
|
40
|
-
|
|
41
|
-
expect(Object.keys(dataSource.getAllPages()).length).to.equal(1);
|
|
42
|
-
expect(dataSource.getPage(1).length).to.equal(2);
|
|
43
|
-
expect(dataSource.getPage(1)[0].identifier).to.equal('foo');
|
|
44
|
-
expect(dataSource.getPage(1)[1].identifier).to.equal('bar');
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('can add data split across multiple pages', () => {
|
|
48
|
-
const dataSource = new CollectionBrowserDataSource(host, 3);
|
|
49
|
-
const doubledDataPage = [...dataPage, ...dataPage];
|
|
50
|
-
dataSource.addMultiplePages(1, doubledDataPage);
|
|
51
|
-
|
|
52
|
-
expect(Object.keys(dataSource.getAllPages()).length).to.equal(2);
|
|
53
|
-
expect(dataSource.getPage(1).length).to.equal(3);
|
|
54
|
-
expect(dataSource.getPage(2).length).to.equal(1);
|
|
55
|
-
expect(dataSource.getPage(1)[0].identifier).to.equal('foo');
|
|
56
|
-
expect(dataSource.getPage(1)[1].identifier).to.equal('bar');
|
|
57
|
-
expect(dataSource.getPage(1)[2].identifier).to.equal('foo');
|
|
58
|
-
expect(dataSource.getPage(2)[0].identifier).to.equal('bar');
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
it('resets data when changing page size', () => {
|
|
62
|
-
const dataSource = new CollectionBrowserDataSource(host);
|
|
63
|
-
dataSource.addPage(1, dataPage);
|
|
64
|
-
|
|
65
|
-
dataSource.setPageSize(100);
|
|
66
|
-
expect(Object.keys(dataSource.getAllPages()).length).to.equal(0);
|
|
67
|
-
expect(dataSource.getPageSize()).to.equal(100);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it('can be installed on the host', async () => {
|
|
71
|
-
const dataSource = new CollectionBrowserDataSource(host);
|
|
72
|
-
dataSource.addPage(1, dataPage);
|
|
73
|
-
|
|
74
|
-
host.installDataSourceAndQueryState(dataSource, {
|
|
75
|
-
searchType: SearchType.METADATA,
|
|
76
|
-
sortDirection: null,
|
|
77
|
-
selectedTitleFilter: null,
|
|
78
|
-
selectedCreatorFilter: null,
|
|
79
|
-
baseQuery: 'foobar',
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
await host.updateComplete;
|
|
83
|
-
|
|
84
|
-
expect(host.dataSource).to.equal(dataSource);
|
|
85
|
-
expect(host.baseQuery).to.equal('foobar');
|
|
86
|
-
|
|
87
|
-
host.removeController(dataSource);
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
it('can suppress further fetches', async () => {
|
|
91
|
-
host.searchService = new MockSearchService();
|
|
92
|
-
|
|
93
|
-
const pageFetchSpy = sinon.spy();
|
|
94
|
-
const dataSource = new CollectionBrowserDataSource(host);
|
|
95
|
-
dataSource.fetchPage = pageFetchSpy;
|
|
96
|
-
|
|
97
|
-
dataSource.addPage(1, dataPage);
|
|
98
|
-
dataSource.setFetchesSuppressed(true);
|
|
99
|
-
dataSource.handleQueryChange();
|
|
100
|
-
|
|
101
|
-
expect(pageFetchSpy.callCount).to.equal(0);
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
it('can set its initial page batch size', async () => {
|
|
105
|
-
host.searchService = new MockSearchService();
|
|
106
|
-
|
|
107
|
-
const pageFetchSpy = sinon.spy();
|
|
108
|
-
const dataSource = new CollectionBrowserDataSource(host);
|
|
109
|
-
dataSource.setNumInitialPages(10);
|
|
110
|
-
dataSource.fetchPage = pageFetchSpy;
|
|
111
|
-
|
|
112
|
-
dataSource.handleQueryChange();
|
|
113
|
-
|
|
114
|
-
// Uses specified number of initial pages
|
|
115
|
-
expect(pageFetchSpy.args[0][1]).to.equal(10);
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
it('refreshes prefix filter counts', () => {
|
|
119
|
-
const dataSource = new CollectionBrowserDataSource(host);
|
|
120
|
-
dataSource.addPage(1, dataPage);
|
|
121
|
-
|
|
122
|
-
dataSource.prefixFilterCountMap = {
|
|
123
|
-
title: {
|
|
124
|
-
X: 10,
|
|
125
|
-
},
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
dataSource.refreshLetterCounts();
|
|
129
|
-
expect(dataSource.prefixFilterCountMap).to.deep.equal({});
|
|
130
|
-
});
|
|
131
|
-
|
|
1
|
+
import { expect, fixture } from '@open-wc/testing';
|
|
2
|
+
import { html } from 'lit';
|
|
3
|
+
import sinon from 'sinon';
|
|
4
|
+
import { ItemHit, SearchType } from '@internetarchive/search-service';
|
|
5
|
+
import { CollectionBrowserDataSource } from '../../src/data-source/collection-browser-data-source';
|
|
6
|
+
import { TileModel } from '../../src/models';
|
|
7
|
+
import type { CollectionBrowser } from '../../src/collection-browser';
|
|
8
|
+
import '../../src/collection-browser';
|
|
9
|
+
import { MockSearchService } from '../mocks/mock-search-service';
|
|
10
|
+
|
|
11
|
+
const dataPage: TileModel[] = [
|
|
12
|
+
new TileModel(
|
|
13
|
+
new ItemHit({
|
|
14
|
+
fields: {
|
|
15
|
+
identifier: 'foo',
|
|
16
|
+
},
|
|
17
|
+
}),
|
|
18
|
+
),
|
|
19
|
+
new TileModel(
|
|
20
|
+
new ItemHit({
|
|
21
|
+
fields: {
|
|
22
|
+
identifier: 'bar',
|
|
23
|
+
},
|
|
24
|
+
}),
|
|
25
|
+
),
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
describe('Collection Browser Data Source', () => {
|
|
29
|
+
let host: CollectionBrowser;
|
|
30
|
+
|
|
31
|
+
beforeEach(async () => {
|
|
32
|
+
host = await fixture<CollectionBrowser>(html`
|
|
33
|
+
<collection-browser></collection-browser>
|
|
34
|
+
`);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('can add and retrieve data pages', () => {
|
|
38
|
+
const dataSource = new CollectionBrowserDataSource(host);
|
|
39
|
+
dataSource.addPage(1, dataPage);
|
|
40
|
+
|
|
41
|
+
expect(Object.keys(dataSource.getAllPages()).length).to.equal(1);
|
|
42
|
+
expect(dataSource.getPage(1).length).to.equal(2);
|
|
43
|
+
expect(dataSource.getPage(1)[0].identifier).to.equal('foo');
|
|
44
|
+
expect(dataSource.getPage(1)[1].identifier).to.equal('bar');
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('can add data split across multiple pages', () => {
|
|
48
|
+
const dataSource = new CollectionBrowserDataSource(host, 3);
|
|
49
|
+
const doubledDataPage = [...dataPage, ...dataPage];
|
|
50
|
+
dataSource.addMultiplePages(1, doubledDataPage);
|
|
51
|
+
|
|
52
|
+
expect(Object.keys(dataSource.getAllPages()).length).to.equal(2);
|
|
53
|
+
expect(dataSource.getPage(1).length).to.equal(3);
|
|
54
|
+
expect(dataSource.getPage(2).length).to.equal(1);
|
|
55
|
+
expect(dataSource.getPage(1)[0].identifier).to.equal('foo');
|
|
56
|
+
expect(dataSource.getPage(1)[1].identifier).to.equal('bar');
|
|
57
|
+
expect(dataSource.getPage(1)[2].identifier).to.equal('foo');
|
|
58
|
+
expect(dataSource.getPage(2)[0].identifier).to.equal('bar');
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('resets data when changing page size', () => {
|
|
62
|
+
const dataSource = new CollectionBrowserDataSource(host);
|
|
63
|
+
dataSource.addPage(1, dataPage);
|
|
64
|
+
|
|
65
|
+
dataSource.setPageSize(100);
|
|
66
|
+
expect(Object.keys(dataSource.getAllPages()).length).to.equal(0);
|
|
67
|
+
expect(dataSource.getPageSize()).to.equal(100);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('can be installed on the host', async () => {
|
|
71
|
+
const dataSource = new CollectionBrowserDataSource(host);
|
|
72
|
+
dataSource.addPage(1, dataPage);
|
|
73
|
+
|
|
74
|
+
host.installDataSourceAndQueryState(dataSource, {
|
|
75
|
+
searchType: SearchType.METADATA,
|
|
76
|
+
sortDirection: null,
|
|
77
|
+
selectedTitleFilter: null,
|
|
78
|
+
selectedCreatorFilter: null,
|
|
79
|
+
baseQuery: 'foobar',
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
await host.updateComplete;
|
|
83
|
+
|
|
84
|
+
expect(host.dataSource).to.equal(dataSource);
|
|
85
|
+
expect(host.baseQuery).to.equal('foobar');
|
|
86
|
+
|
|
87
|
+
host.removeController(dataSource);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('can suppress further fetches', async () => {
|
|
91
|
+
host.searchService = new MockSearchService();
|
|
92
|
+
|
|
93
|
+
const pageFetchSpy = sinon.spy();
|
|
94
|
+
const dataSource = new CollectionBrowserDataSource(host);
|
|
95
|
+
dataSource.fetchPage = pageFetchSpy;
|
|
96
|
+
|
|
97
|
+
dataSource.addPage(1, dataPage);
|
|
98
|
+
dataSource.setFetchesSuppressed(true);
|
|
99
|
+
dataSource.handleQueryChange();
|
|
100
|
+
|
|
101
|
+
expect(pageFetchSpy.callCount).to.equal(0);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it('can set its initial page batch size', async () => {
|
|
105
|
+
host.searchService = new MockSearchService();
|
|
106
|
+
|
|
107
|
+
const pageFetchSpy = sinon.spy();
|
|
108
|
+
const dataSource = new CollectionBrowserDataSource(host);
|
|
109
|
+
dataSource.setNumInitialPages(10);
|
|
110
|
+
dataSource.fetchPage = pageFetchSpy;
|
|
111
|
+
|
|
112
|
+
dataSource.handleQueryChange();
|
|
113
|
+
|
|
114
|
+
// Uses specified number of initial pages
|
|
115
|
+
expect(pageFetchSpy.args[0][1]).to.equal(10);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
it('refreshes prefix filter counts', () => {
|
|
119
|
+
const dataSource = new CollectionBrowserDataSource(host);
|
|
120
|
+
dataSource.addPage(1, dataPage);
|
|
121
|
+
|
|
122
|
+
dataSource.prefixFilterCountMap = {
|
|
123
|
+
title: {
|
|
124
|
+
X: 10,
|
|
125
|
+
},
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
dataSource.refreshLetterCounts();
|
|
129
|
+
expect(dataSource.prefixFilterCountMap).to.deep.equal({});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
describe('empty FTS query in collection falls back to metadata search', () => {
|
|
133
|
+
it('allows search with empty query and FTS in a collection', async () => {
|
|
134
|
+
const searchService = new MockSearchService();
|
|
135
|
+
host.searchService = searchService;
|
|
136
|
+
host.withinCollection = 'test-collection';
|
|
137
|
+
host.searchType = SearchType.FULLTEXT;
|
|
138
|
+
host.baseQuery = '';
|
|
139
|
+
await host.updateComplete;
|
|
140
|
+
|
|
141
|
+
const dataSource = new CollectionBrowserDataSource(host);
|
|
142
|
+
host.addController(dataSource);
|
|
143
|
+
expect(dataSource.canPerformSearch).to.be.true;
|
|
144
|
+
host.removeController(dataSource);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
it('uses metadata search type for fetch when FTS query is empty in a collection', async () => {
|
|
148
|
+
const searchService = new MockSearchService();
|
|
149
|
+
host.searchService = searchService;
|
|
150
|
+
host.withinCollection = 'test-collection';
|
|
151
|
+
host.searchType = SearchType.FULLTEXT;
|
|
152
|
+
host.baseQuery = '';
|
|
153
|
+
await host.updateComplete;
|
|
154
|
+
|
|
155
|
+
const dataSource = new CollectionBrowserDataSource(host);
|
|
156
|
+
host.addController(dataSource);
|
|
157
|
+
await dataSource.fetchPage(1);
|
|
158
|
+
|
|
159
|
+
expect(searchService.searchType).to.equal(SearchType.METADATA);
|
|
160
|
+
host.removeController(dataSource);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
it('uses FTS search type for fetch when FTS query is non-empty in a collection', async () => {
|
|
164
|
+
const searchService = new MockSearchService();
|
|
165
|
+
host.searchService = searchService;
|
|
166
|
+
host.withinCollection = 'test-collection';
|
|
167
|
+
host.searchType = SearchType.FULLTEXT;
|
|
168
|
+
host.baseQuery = 'some query';
|
|
169
|
+
await host.updateComplete;
|
|
170
|
+
|
|
171
|
+
const dataSource = new CollectionBrowserDataSource(host);
|
|
172
|
+
host.addController(dataSource);
|
|
173
|
+
await dataSource.fetchPage(1);
|
|
174
|
+
|
|
175
|
+
expect(searchService.searchType).to.equal(SearchType.FULLTEXT);
|
|
176
|
+
host.removeController(dataSource);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('does not allow search with empty FTS query outside a collection', async () => {
|
|
180
|
+
const searchService = new MockSearchService();
|
|
181
|
+
host.searchService = searchService;
|
|
182
|
+
host.withinCollection = undefined;
|
|
183
|
+
host.searchType = SearchType.FULLTEXT;
|
|
184
|
+
host.baseQuery = '';
|
|
185
|
+
await host.updateComplete;
|
|
186
|
+
|
|
187
|
+
const dataSource = new CollectionBrowserDataSource(host);
|
|
188
|
+
host.addController(dataSource);
|
|
189
|
+
expect(dataSource.canPerformSearch).to.be.false;
|
|
190
|
+
host.removeController(dataSource);
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
});
|