@internetarchive/collection-browser 4.3.0 → 4.3.1-alpha-webdev8257.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.js.map +1 -1
  3. package/dist/src/app-root.d.ts +11 -0
  4. package/dist/src/app-root.js +107 -0
  5. package/dist/src/app-root.js.map +1 -1
  6. package/dist/src/collection-browser.d.ts +8 -4
  7. package/dist/src/collection-browser.js +19 -20
  8. package/dist/src/collection-browser.js.map +1 -1
  9. package/dist/src/data-source/collection-browser-data-source.js.map +1 -1
  10. package/dist/src/manage/manage-bar.d.ts +7 -2
  11. package/dist/src/manage/manage-bar.js +25 -3
  12. package/dist/src/manage/manage-bar.js.map +1 -1
  13. package/dist/src/styles/tile-action-styles.d.ts +14 -0
  14. package/dist/src/styles/tile-action-styles.js +52 -0
  15. package/dist/src/styles/tile-action-styles.js.map +1 -0
  16. package/dist/src/tiles/base-tile-component.d.ts +17 -1
  17. package/dist/src/tiles/base-tile-component.js +48 -1
  18. package/dist/src/tiles/base-tile-component.js.map +1 -1
  19. package/dist/src/tiles/grid/item-tile.js +1 -0
  20. package/dist/src/tiles/grid/item-tile.js.map +1 -1
  21. package/dist/src/tiles/list/tile-list-compact-header.js +66 -46
  22. package/dist/src/tiles/list/tile-list-compact-header.js.map +1 -1
  23. package/dist/src/tiles/list/tile-list-compact.d.ts +1 -1
  24. package/dist/src/tiles/list/tile-list-compact.js +132 -100
  25. package/dist/src/tiles/list/tile-list-compact.js.map +1 -1
  26. package/dist/src/tiles/list/tile-list.d.ts +1 -1
  27. package/dist/src/tiles/list/tile-list.js +316 -298
  28. package/dist/src/tiles/list/tile-list.js.map +1 -1
  29. package/dist/src/tiles/models.d.ts +14 -0
  30. package/dist/src/tiles/models.js.map +1 -1
  31. package/dist/src/tiles/tile-dispatcher.d.ts +14 -0
  32. package/dist/src/tiles/tile-dispatcher.js +107 -4
  33. package/dist/src/tiles/tile-dispatcher.js.map +1 -1
  34. package/dist/src/tiles/tile-display-value-provider.js.map +1 -1
  35. package/dist/test/data-source/collection-browser-data-source.test.js +2 -2
  36. package/dist/test/data-source/collection-browser-data-source.test.js.map +1 -1
  37. package/dist/test/manage/manage-bar.test.d.ts +1 -0
  38. package/dist/test/manage/manage-bar.test.js +123 -1
  39. package/dist/test/manage/manage-bar.test.js.map +1 -1
  40. package/dist/test/tiles/list/tile-list-compact-header.test.js +12 -12
  41. package/dist/test/tiles/list/tile-list-compact-header.test.js.map +1 -1
  42. package/dist/test/tiles/list/tile-list.test.js +134 -134
  43. package/dist/test/tiles/list/tile-list.test.js.map +1 -1
  44. package/index.ts +1 -0
  45. package/package.json +1 -1
  46. package/src/app-root.ts +115 -0
  47. package/src/collection-browser.ts +16 -30
  48. package/src/data-source/collection-browser-data-source.ts +1465 -1465
  49. package/src/manage/manage-bar.ts +33 -4
  50. package/src/styles/tile-action-styles.ts +52 -0
  51. package/src/tiles/base-tile-component.ts +57 -1
  52. package/src/tiles/grid/item-tile.ts +1 -0
  53. package/src/tiles/list/tile-list-compact-header.ts +106 -86
  54. package/src/tiles/list/tile-list-compact.ts +273 -239
  55. package/src/tiles/list/tile-list.ts +718 -700
  56. package/src/tiles/models.ts +16 -0
  57. package/src/tiles/tile-dispatcher.ts +114 -4
  58. package/src/tiles/tile-display-value-provider.ts +134 -134
  59. package/test/data-source/collection-browser-data-source.test.ts +193 -193
  60. package/test/manage/manage-bar.test.ts +192 -2
  61. package/test/tiles/list/tile-list-compact-header.test.ts +43 -43
  62. package/test/tiles/list/tile-list.test.ts +576 -576
  63. package/.claude/settings.local.json +0 -11
@@ -1,193 +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
-
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
- });
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
+ });
@@ -2,6 +2,7 @@ import { expect, fixture } from '@open-wc/testing';
2
2
  import { html } from 'lit';
3
3
  import Sinon from 'sinon';
4
4
  import '../../src/manage/manage-bar';
5
+ import '../../src/manage/remove-items-modal-content';
5
6
  import {
6
7
  ModalManager,
7
8
  ModalManagerInterface,
@@ -9,6 +10,8 @@ import {
9
10
  import '@internetarchive/modal-manager';
10
11
  import { msg } from '@lit/localize';
11
12
  import type { ManageBar } from '../../src/manage/manage-bar';
13
+ import type { PageElementName } from '@internetarchive/search-service';
14
+ import type { RemoveItemsModalContent } from '../../src/manage/remove-items-modal-content';
12
15
 
13
16
  describe('Manage bar', () => {
14
17
  it('renders basic component', async () => {
@@ -145,8 +148,6 @@ describe('Manage bar', () => {
145
148
  await el.updateComplete;
146
149
  removeButton?.click();
147
150
 
148
- console.log(showModalSpy.args[0][0].config.title?.values[0]);
149
-
150
151
  expect(showModalSpy.callCount).to.equal(1);
151
152
  expect(el.modalManager?.classList.contains('remove-items')).to.be;
152
153
  expect(showModalSpy.args[0][0].config.title?.values[0]).to.equal(
@@ -154,4 +155,193 @@ describe('Manage bar', () => {
154
155
  );
155
156
  expect(showModalSpy.args[0][0].customModalContent).to.exist;
156
157
  });
158
+
159
+ it('shows selected items count in remove button', async () => {
160
+ const el = await fixture<ManageBar>(html`
161
+ <manage-bar
162
+ .selectedItems=${[
163
+ { identifier: '1', title: 'Item 1' },
164
+ { identifier: '2', title: 'Item 2' },
165
+ { identifier: '3', title: 'Item 3' },
166
+ ]}
167
+ ></manage-bar>
168
+ `);
169
+ const removeBtn = el.shadowRoot?.querySelector('.ia-button.danger');
170
+ expect(removeBtn?.textContent?.trim()).to.include('(3)');
171
+ });
172
+
173
+ it('Item Manager button is disabled when removeAllowed is false', async () => {
174
+ const el = await fixture<ManageBar>(
175
+ html`<manage-bar showItemManageButton></manage-bar>`,
176
+ );
177
+ expect(el.shadowRoot?.querySelector('.ia-button.warning:disabled')).to
178
+ .exist;
179
+
180
+ el.removeAllowed = true;
181
+ await el.updateComplete;
182
+
183
+ expect(el.shadowRoot?.querySelector('.ia-button.warning:disabled')).not.to
184
+ .exist;
185
+ });
186
+
187
+ it('emits manageItems event when Item Manager button clicked', async () => {
188
+ const spy = Sinon.spy();
189
+ const el = await fixture<ManageBar>(html`
190
+ <manage-bar
191
+ showItemManageButton
192
+ removeAllowed
193
+ @manageItems=${spy}
194
+ ></manage-bar>
195
+ `);
196
+ const manageBtn = el.shadowRoot?.querySelector(
197
+ '.ia-button.warning',
198
+ ) as HTMLButtonElement;
199
+ expect(manageBtn).to.exist;
200
+ manageBtn.click();
201
+ expect(spy.callCount).to.equal(1);
202
+ });
203
+
204
+ it('emits removeItems event when modal confirm is clicked', async () => {
205
+ const modalManager = await fixture<ModalManager>(
206
+ html`<modal-manager></modal-manager>`,
207
+ );
208
+ const removeItemsSpy = Sinon.spy();
209
+ const el = await fixture<ManageBar>(html`
210
+ <manage-bar
211
+ .modalManager=${modalManager}
212
+ .selectedItems=${[{ identifier: '1', title: 'Item 1' }]}
213
+ removeAllowed
214
+ @removeItems=${removeItemsSpy}
215
+ ></manage-bar>
216
+ `);
217
+ await el.updateComplete;
218
+
219
+ const showModalSpy = Sinon.spy(
220
+ el.modalManager as ModalManagerInterface,
221
+ 'showModal',
222
+ );
223
+ (
224
+ el.shadowRoot?.querySelector('.ia-button.danger') as HTMLButtonElement
225
+ ).click();
226
+
227
+ const contentEl = (await fixture(
228
+ showModalSpy.args[0][0].customModalContent!,
229
+ )) as RemoveItemsModalContent;
230
+ (
231
+ contentEl.shadowRoot?.querySelector(
232
+ '.remove-items-btn',
233
+ ) as HTMLButtonElement
234
+ ).click();
235
+
236
+ expect(removeItemsSpy.callCount).to.equal(1);
237
+ });
238
+
239
+ describe('profileElement modal messages', () => {
240
+ async function openModalContent(
241
+ profileElement: string,
242
+ itemCount = 1,
243
+ ): Promise<RemoveItemsModalContent> {
244
+ const modalManager = await fixture<ModalManager>(
245
+ html`<modal-manager></modal-manager>`,
246
+ );
247
+ const items = Array.from({ length: itemCount }, (_, i) => ({
248
+ identifier: String(i + 1),
249
+ title: `Item ${i + 1}`,
250
+ }));
251
+ const el = await fixture<ManageBar>(html`
252
+ <manage-bar
253
+ .selectedItems=${items}
254
+ .profileElement=${profileElement as PageElementName}
255
+ removeAllowed
256
+ ></manage-bar>
257
+ `);
258
+ el.modalManager = modalManager as ModalManagerInterface;
259
+ await el.updateComplete;
260
+
261
+ const showModalSpy = Sinon.spy(
262
+ el.modalManager as ModalManagerInterface,
263
+ 'showModal',
264
+ );
265
+ (
266
+ el.shadowRoot?.querySelector('.ia-button.danger') as HTMLButtonElement
267
+ ).click();
268
+
269
+ return (await fixture(
270
+ showModalSpy.args[0][0].customModalContent!,
271
+ )) as RemoveItemsModalContent;
272
+ }
273
+
274
+ it('shows uploads-specific message for profileElement=uploads', async () => {
275
+ const contentEl = await openModalContent('uploads');
276
+ expect(contentEl.message).to.include('uploads list');
277
+ });
278
+
279
+ it('shows web archives message for profileElement=web_archives', async () => {
280
+ const contentEl = await openModalContent('web_archives');
281
+ expect(contentEl.message).to.include('web archives list');
282
+ });
283
+
284
+ it('shows favorites message for profileElement=favorites', async () => {
285
+ const contentEl = await openModalContent('favorites');
286
+ expect(contentEl.message).to.include('favorites list');
287
+ });
288
+
289
+ it('shows no message for unmapped profileElement (e.g. reviews)', async () => {
290
+ const contentEl = await openModalContent('reviews');
291
+ expect(contentEl.message).to.equal('');
292
+ expect(contentEl.shadowRoot?.querySelector('.message')).not.to.exist;
293
+ });
294
+
295
+ it('uses "this item" for a single selected item', async () => {
296
+ const contentEl = await openModalContent('uploads', 1);
297
+ expect(contentEl.message).to.include('this item');
298
+ });
299
+
300
+ it('uses "these items" for multiple selected items', async () => {
301
+ const contentEl = await openModalContent('uploads', 2);
302
+ expect(contentEl.message).to.include('these items');
303
+ });
304
+ });
305
+
306
+ it('showRemoveItemsProcessingModal shows processing modal', async () => {
307
+ const modalManager = await fixture<ModalManager>(
308
+ html`<modal-manager></modal-manager>`,
309
+ );
310
+ const el = await fixture<ManageBar>(html`
311
+ <manage-bar .modalManager=${modalManager}></manage-bar>
312
+ `);
313
+ const showModalSpy = Sinon.spy(
314
+ el.modalManager as ModalManagerInterface,
315
+ 'showModal',
316
+ );
317
+
318
+ el.showRemoveItemsProcessingModal();
319
+
320
+ expect(showModalSpy.callCount).to.equal(1);
321
+ expect(showModalSpy.args[0][0].config.showProcessingIndicator).to.be.true;
322
+ expect(showModalSpy.args[0][0].config.title?.values[0]).to.equal(
323
+ msg('Removing selected items...'),
324
+ );
325
+ });
326
+
327
+ it('showRemoveItemsErrorModal shows error modal', async () => {
328
+ const modalManager = await fixture<ModalManager>(
329
+ html`<modal-manager></modal-manager>`,
330
+ );
331
+ const el = await fixture<ManageBar>(html`
332
+ <manage-bar .modalManager=${modalManager}></manage-bar>
333
+ `);
334
+ const showModalSpy = Sinon.spy(
335
+ el.modalManager as ModalManagerInterface,
336
+ 'showModal',
337
+ );
338
+
339
+ el.showRemoveItemsErrorModal();
340
+
341
+ expect(showModalSpy.callCount).to.equal(1);
342
+ expect(showModalSpy.args[0][0].config.showProcessingIndicator).to.be.false;
343
+ expect(showModalSpy.args[0][0].config.title?.values[0]).to.equal(
344
+ msg('Error: unable to remove items'),
345
+ );
346
+ });
157
347
  });