@internetarchive/collection-browser 2.17.1-alpha-webdev7667.0 → 2.18.1-alpha-webdev7768.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 (45) hide show
  1. package/.github/workflows/ci.yml +27 -27
  2. package/.github/workflows/gh-pages-main.yml +39 -39
  3. package/.github/workflows/npm-publish.yml +39 -39
  4. package/.github/workflows/pr-preview.yml +38 -38
  5. package/.prettierignore +1 -1
  6. package/dist/src/app-root.d.ts +0 -3
  7. package/dist/src/app-root.js +0 -91
  8. package/dist/src/app-root.js.map +1 -1
  9. package/dist/src/collection-browser.js +682 -682
  10. package/dist/src/collection-browser.js.map +1 -1
  11. package/dist/src/collection-facets.js +259 -259
  12. package/dist/src/collection-facets.js.map +1 -1
  13. package/dist/src/expanded-date-picker.js +50 -50
  14. package/dist/src/expanded-date-picker.js.map +1 -1
  15. package/dist/src/tiles/grid/styles/tile-grid-shared-styles.js +1 -1
  16. package/dist/src/tiles/grid/styles/tile-grid-shared-styles.js.map +1 -1
  17. package/dist/src/tiles/tile-dispatcher.js +1 -1
  18. package/dist/src/tiles/tile-dispatcher.js.map +1 -1
  19. package/dist/test/collection-browser.test.js +58 -3
  20. package/dist/test/collection-browser.test.js.map +1 -1
  21. package/dist/test/image-block.test.js +21 -0
  22. package/dist/test/image-block.test.js.map +1 -1
  23. package/dist/test/item-image.test.js +73 -0
  24. package/dist/test/item-image.test.js.map +1 -1
  25. package/dist/test/mocks/mock-search-responses.d.ts +2 -0
  26. package/dist/test/mocks/mock-search-responses.js +96 -0
  27. package/dist/test/mocks/mock-search-responses.js.map +1 -1
  28. package/dist/test/mocks/mock-search-service.d.ts +1 -0
  29. package/dist/test/mocks/mock-search-service.js +7 -1
  30. package/dist/test/mocks/mock-search-service.js.map +1 -1
  31. package/eslint.config.mjs +53 -53
  32. package/index.html +24 -24
  33. package/package.json +117 -117
  34. package/src/app-root.ts +0 -97
  35. package/src/collection-browser.ts +2712 -2712
  36. package/src/collection-facets.ts +966 -966
  37. package/src/expanded-date-picker.ts +175 -175
  38. package/src/tiles/grid/styles/tile-grid-shared-styles.ts +1 -1
  39. package/src/tiles/tile-dispatcher.ts +1 -1
  40. package/test/collection-browser.test.ts +86 -2
  41. package/test/image-block.test.ts +24 -0
  42. package/test/item-image.test.ts +86 -0
  43. package/test/mocks/mock-search-responses.ts +104 -0
  44. package/test/mocks/mock-search-service.ts +11 -0
  45. package/tsconfig.json +20 -20
@@ -1,175 +1,175 @@
1
- import { css, html, LitElement, CSSResultGroup, TemplateResult } from 'lit';
2
- import { customElement, property } from 'lit/decorators.js';
3
- import { ifDefined } from 'lit/directives/if-defined.js';
4
- import { msg } from '@lit/localize';
5
- import type { ModalManagerInterface } from '@internetarchive/modal-manager';
6
- import type { AnalyticsManagerInterface } from '@internetarchive/analytics-manager';
7
- import { BinSnappingInterval } from '@internetarchive/histogram-date-range';
8
- import {
9
- analyticsActions,
10
- analyticsCategories,
11
- } from './utils/analytics-events';
12
-
13
- import '@internetarchive/histogram-date-range';
14
-
15
- @customElement('expanded-date-picker')
16
- export class ExpandedDatePicker extends LitElement {
17
- @property({ type: String }) minDate?: string;
18
-
19
- @property({ type: String }) maxDate?: string;
20
-
21
- @property({ type: String }) minSelectedDate?: string;
22
-
23
- @property({ type: String }) maxSelectedDate?: string;
24
-
25
- @property({ type: Array }) buckets?: number[];
26
-
27
- @property({ type: String }) dateFormat: string = 'YYYY';
28
-
29
- @property({ type: String }) tooltipDateFormat?: string;
30
-
31
- @property({ type: String }) binSnapping?: BinSnappingInterval;
32
-
33
- @property({ type: Object, attribute: false })
34
- modalManager?: ModalManagerInterface;
35
-
36
- @property({ type: Object, attribute: false })
37
- analyticsHandler?: AnalyticsManagerInterface;
38
-
39
- render(): TemplateResult {
40
- return html`
41
- <div id="container">
42
- <histogram-date-range
43
- id="date-picker"
44
- .minDate=${this.minDate}
45
- .maxDate=${this.maxDate}
46
- .minSelectedDate=${this.minSelectedDate ?? this.minDate}
47
- .maxSelectedDate=${this.maxSelectedDate ?? this.maxDate}
48
- .dateFormat=${this.dateFormat}
49
- tooltipDateFormat=${ifDefined(this.tooltipDateFormat)}
50
- binSnapping=${ifDefined(this.binSnapping)}
51
- .updateDelay=${0}
52
- updateWhileFocused
53
- missingDataMessage="..."
54
- .width=${560}
55
- .height=${120}
56
- .bins=${this.buckets}
57
- @histogramDateRangeUpdated=${this.histogramDateRangeUpdated}
58
- >
59
- <button
60
- id="apply-btn"
61
- slot="inputs-right-side"
62
- @click=${this.applyBtnClicked}
63
- >
64
- ${msg('Apply date range')}
65
- </button>
66
- </histogram-date-range>
67
- </div>
68
- `;
69
- }
70
-
71
- connectedCallback(): void {
72
- super.connectedCallback?.();
73
- this.setupEscapeListener();
74
- }
75
-
76
- disconnectedCallback(): void {
77
- super.disconnectedCallback?.();
78
- this.removeEscapeListener();
79
- }
80
-
81
- /**
82
- * Add an event listener to close the date picker modal when the Esc key is pressed
83
- */
84
- private setupEscapeListener() {
85
- document.addEventListener('keydown', this.boundEscapeListener);
86
- }
87
-
88
- /**
89
- * Remove the Esc key listener that was previously added
90
- */
91
- private removeEscapeListener() {
92
- document.removeEventListener('keydown', this.boundEscapeListener);
93
- }
94
-
95
- /**
96
- * Closes the modal dialog if the given event is pressing the Esc key.
97
- * Arrow function to ensure `this` remains bound to the current component.
98
- */
99
- private boundEscapeListener = (e: KeyboardEvent) => {
100
- if (e.key === 'Escape') {
101
- this.closeModal();
102
- }
103
- };
104
-
105
- /**
106
- * When the histogram is updated, keep track of the newly selected date range.
107
- * We don't commit to the new range until the apply button is clicked.
108
- */
109
- private histogramDateRangeUpdated(
110
- e: CustomEvent<{
111
- minDate: string;
112
- maxDate: string;
113
- }>,
114
- ): void {
115
- this.minSelectedDate = e.detail.minDate;
116
- this.maxSelectedDate = e.detail.maxDate;
117
- }
118
-
119
- /**
120
- * When the Apply button is clicked, emit the current date range and close the modal.
121
- */
122
- private applyBtnClicked(): void {
123
- const event = new CustomEvent('histogramDateRangeApplied', {
124
- detail: {
125
- minDate: this.minSelectedDate,
126
- maxDate: this.maxSelectedDate,
127
- },
128
- });
129
- this.dispatchEvent(event);
130
- this.closeModal();
131
-
132
- this.analyticsHandler?.sendEvent({
133
- category: analyticsCategories.default,
134
- action: analyticsActions.histogramChangedFromModal,
135
- label: window.location.href,
136
- });
137
- }
138
-
139
- /**
140
- * Closes the modal associated with this component (if it exists) and dispatches a
141
- * modalClosed event.
142
- */
143
- private closeModal(): void {
144
- if (this.modalManager) {
145
- this.modalManager.closeModal();
146
- this.dispatchEvent(new CustomEvent('modalClosed'));
147
- }
148
- }
149
-
150
- static get styles(): CSSResultGroup {
151
- return css`
152
- #container {
153
- display: flex;
154
- justify-content: center;
155
- padding: 40px 10px 10px;
156
- overflow: hidden;
157
- }
158
-
159
- #date-picker {
160
- --histogramDateRangeInputWidth: 50px;
161
- --histogramDateRangeInputRowMargin: 5px 0 0 0;
162
- }
163
-
164
- #apply-btn {
165
- margin: 0 0 0 5px;
166
- padding: 8px 10px;
167
- border: 0;
168
- border-radius: 4px;
169
- background: var(--primaryButtonBGColor, #194880);
170
- color: white;
171
- cursor: pointer;
172
- }
173
- `;
174
- }
175
- }
1
+ import { css, html, LitElement, CSSResultGroup, TemplateResult } from 'lit';
2
+ import { customElement, property } from 'lit/decorators.js';
3
+ import { ifDefined } from 'lit/directives/if-defined.js';
4
+ import { msg } from '@lit/localize';
5
+ import type { ModalManagerInterface } from '@internetarchive/modal-manager';
6
+ import type { AnalyticsManagerInterface } from '@internetarchive/analytics-manager';
7
+ import { BinSnappingInterval } from '@internetarchive/histogram-date-range';
8
+ import {
9
+ analyticsActions,
10
+ analyticsCategories,
11
+ } from './utils/analytics-events';
12
+
13
+ import '@internetarchive/histogram-date-range';
14
+
15
+ @customElement('expanded-date-picker')
16
+ export class ExpandedDatePicker extends LitElement {
17
+ @property({ type: String }) minDate?: string;
18
+
19
+ @property({ type: String }) maxDate?: string;
20
+
21
+ @property({ type: String }) minSelectedDate?: string;
22
+
23
+ @property({ type: String }) maxSelectedDate?: string;
24
+
25
+ @property({ type: Array }) buckets?: number[];
26
+
27
+ @property({ type: String }) dateFormat: string = 'YYYY';
28
+
29
+ @property({ type: String }) tooltipDateFormat?: string;
30
+
31
+ @property({ type: String }) binSnapping?: BinSnappingInterval;
32
+
33
+ @property({ type: Object, attribute: false })
34
+ modalManager?: ModalManagerInterface;
35
+
36
+ @property({ type: Object, attribute: false })
37
+ analyticsHandler?: AnalyticsManagerInterface;
38
+
39
+ render(): TemplateResult {
40
+ return html`
41
+ <div id="container">
42
+ <histogram-date-range
43
+ id="date-picker"
44
+ .minDate=${this.minDate}
45
+ .maxDate=${this.maxDate}
46
+ .minSelectedDate=${this.minSelectedDate ?? this.minDate}
47
+ .maxSelectedDate=${this.maxSelectedDate ?? this.maxDate}
48
+ .dateFormat=${this.dateFormat}
49
+ tooltipDateFormat=${ifDefined(this.tooltipDateFormat)}
50
+ binSnapping=${ifDefined(this.binSnapping)}
51
+ .updateDelay=${0}
52
+ updateWhileFocused
53
+ missingDataMessage="..."
54
+ .width=${560}
55
+ .height=${120}
56
+ .bins=${this.buckets}
57
+ @histogramDateRangeUpdated=${this.histogramDateRangeUpdated}
58
+ >
59
+ <button
60
+ id="apply-btn"
61
+ slot="inputs-right-side"
62
+ @click=${this.applyBtnClicked}
63
+ >
64
+ ${msg('Apply date range')}
65
+ </button>
66
+ </histogram-date-range>
67
+ </div>
68
+ `;
69
+ }
70
+
71
+ connectedCallback(): void {
72
+ super.connectedCallback?.();
73
+ this.setupEscapeListener();
74
+ }
75
+
76
+ disconnectedCallback(): void {
77
+ super.disconnectedCallback?.();
78
+ this.removeEscapeListener();
79
+ }
80
+
81
+ /**
82
+ * Add an event listener to close the date picker modal when the Esc key is pressed
83
+ */
84
+ private setupEscapeListener() {
85
+ document.addEventListener('keydown', this.boundEscapeListener);
86
+ }
87
+
88
+ /**
89
+ * Remove the Esc key listener that was previously added
90
+ */
91
+ private removeEscapeListener() {
92
+ document.removeEventListener('keydown', this.boundEscapeListener);
93
+ }
94
+
95
+ /**
96
+ * Closes the modal dialog if the given event is pressing the Esc key.
97
+ * Arrow function to ensure `this` remains bound to the current component.
98
+ */
99
+ private boundEscapeListener = (e: KeyboardEvent) => {
100
+ if (e.key === 'Escape') {
101
+ this.closeModal();
102
+ }
103
+ };
104
+
105
+ /**
106
+ * When the histogram is updated, keep track of the newly selected date range.
107
+ * We don't commit to the new range until the apply button is clicked.
108
+ */
109
+ private histogramDateRangeUpdated(
110
+ e: CustomEvent<{
111
+ minDate: string;
112
+ maxDate: string;
113
+ }>,
114
+ ): void {
115
+ this.minSelectedDate = e.detail.minDate;
116
+ this.maxSelectedDate = e.detail.maxDate;
117
+ }
118
+
119
+ /**
120
+ * When the Apply button is clicked, emit the current date range and close the modal.
121
+ */
122
+ private applyBtnClicked(): void {
123
+ const event = new CustomEvent('histogramDateRangeApplied', {
124
+ detail: {
125
+ minDate: this.minSelectedDate,
126
+ maxDate: this.maxSelectedDate,
127
+ },
128
+ });
129
+ this.dispatchEvent(event);
130
+ this.closeModal();
131
+
132
+ this.analyticsHandler?.sendEvent({
133
+ category: analyticsCategories.default,
134
+ action: analyticsActions.histogramChangedFromModal,
135
+ label: window.location.href,
136
+ });
137
+ }
138
+
139
+ /**
140
+ * Closes the modal associated with this component (if it exists) and dispatches a
141
+ * modalClosed event.
142
+ */
143
+ private closeModal(): void {
144
+ if (this.modalManager) {
145
+ this.modalManager.closeModal();
146
+ this.dispatchEvent(new CustomEvent('modalClosed'));
147
+ }
148
+ }
149
+
150
+ static get styles(): CSSResultGroup {
151
+ return css`
152
+ #container {
153
+ display: flex;
154
+ justify-content: center;
155
+ padding: 40px 10px 10px;
156
+ overflow: hidden;
157
+ }
158
+
159
+ #date-picker {
160
+ --histogramDateRangeInputWidth: 50px;
161
+ --histogramDateRangeInputRowMargin: 5px 0 0 0;
162
+ }
163
+
164
+ #apply-btn {
165
+ margin: 0 0 0 5px;
166
+ padding: 8px 10px;
167
+ border: 0;
168
+ border-radius: 4px;
169
+ background: var(--primaryButtonBGColor, #194880);
170
+ color: white;
171
+ cursor: pointer;
172
+ }
173
+ `;
174
+ }
175
+ }
@@ -16,7 +16,7 @@ export const baseTileStyles = css`
16
16
  background-color: ${tileBackgroundColor};
17
17
  border: 1px #2c2c2c;
18
18
  border-radius: ${tileCornerRadius};
19
- box-shadow: 1px 1px 2px 0;
19
+ /*box-shadow: 1px 1px 2px 0;*/
20
20
  box-sizing: border-box;
21
21
  height: 100%;
22
22
  display: flex;
@@ -433,7 +433,7 @@ export class TileDispatcher
433
433
  }
434
434
 
435
435
  #container.hoverable:hover {
436
- box-shadow: 0 0 6px 2px rgba(8, 8, 32, 0.8);
436
+ box-shadow: 0 0 10px 0px rgba(8, 8, 32, 0.15);
437
437
  transition: box-shadow 0.1s ease;
438
438
  }
439
439
 
@@ -895,8 +895,7 @@ describe('Collection Browser', () => {
895
895
  expect(sortSelector, 'sort bar').to.exist;
896
896
 
897
897
  // Click the title sorter
898
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
899
- [...(sortSelector?.children as HTMLCollection & Iterable<any>)] // tsc doesn't know children is iterable
898
+ Array.from(sortSelector!.children)
900
899
  .find(child => child.textContent?.trim() === 'Title')
901
900
  ?.querySelector('button')
902
901
  ?.click();
@@ -904,6 +903,16 @@ describe('Collection Browser', () => {
904
903
  await el.updateComplete;
905
904
 
906
905
  expect(el.selectedSort).to.equal(SortField.title);
906
+
907
+ // Click the creator sorter
908
+ Array.from(sortSelector!.children)
909
+ .find(child => child.textContent?.trim() === 'Creator')
910
+ ?.querySelector('button')
911
+ ?.click();
912
+
913
+ await el.updateComplete;
914
+
915
+ expect(el.selectedSort).to.equal(SortField.creator);
907
916
  });
908
917
 
909
918
  it('sets sort filter properties when user selects title filter', async () => {
@@ -1072,9 +1081,11 @@ describe('Collection Browser', () => {
1072
1081
 
1073
1082
  it('sets date range query when date picker selection changed', async () => {
1074
1083
  const searchService = new MockSearchService();
1084
+ const mockAnalyticsHandler = new MockAnalyticsHandler();
1075
1085
  const el = await fixture<CollectionBrowser>(
1076
1086
  html`<collection-browser
1077
1087
  .searchService=${searchService}
1088
+ .analyticsHandler=${mockAnalyticsHandler}
1078
1089
  .suppressPlaceholders=${true}
1079
1090
  >
1080
1091
  </collection-browser>`,
@@ -2145,6 +2156,79 @@ describe('Collection Browser', () => {
2145
2156
  .exist;
2146
2157
  });
2147
2158
 
2159
+ it('shows Blurring checkbox for admin users', async () => {
2160
+ const searchService = new MockSearchService();
2161
+ const el = await fixture<CollectionBrowser>(
2162
+ html`<collection-browser
2163
+ .baseNavigationUrl=${''}
2164
+ .searchService=${searchService}
2165
+ >
2166
+ </collection-browser>`,
2167
+ );
2168
+
2169
+ el.baseQuery = 'archive-org-user-loggedin';
2170
+ await el.updateComplete;
2171
+ await el.initialSearchComplete;
2172
+
2173
+ const blurringCheck = el.shadowRoot?.querySelector(
2174
+ '#tile-blur-check',
2175
+ ) as HTMLInputElement;
2176
+ expect(blurringCheck).to.exist;
2177
+ expect(blurringCheck.checked).to.be.true;
2178
+ });
2179
+
2180
+ it('unchecks Blurring checkbox for admin users with blurring preference off', async () => {
2181
+ const searchService = new MockSearchService();
2182
+ const el = await fixture<CollectionBrowser>(
2183
+ html`<collection-browser
2184
+ .baseNavigationUrl=${''}
2185
+ .searchService=${searchService}
2186
+ >
2187
+ </collection-browser>`,
2188
+ );
2189
+
2190
+ el.baseQuery = 'archive-org-user-loggedin-noblur';
2191
+ await el.updateComplete;
2192
+ await el.initialSearchComplete;
2193
+
2194
+ const blurringCheck = el.shadowRoot?.querySelector(
2195
+ '#tile-blur-check',
2196
+ ) as HTMLInputElement;
2197
+ expect(blurringCheck).to.exist;
2198
+ expect(blurringCheck.checked).to.be.false;
2199
+ });
2200
+
2201
+ it('toggles blur state when Blurring checkbox is toggled', async () => {
2202
+ const searchService = new MockSearchService();
2203
+ const el = await fixture<CollectionBrowser>(
2204
+ html`<collection-browser
2205
+ .baseNavigationUrl=${''}
2206
+ .searchService=${searchService}
2207
+ >
2208
+ </collection-browser>`,
2209
+ );
2210
+
2211
+ el.baseQuery = 'archive-org-user-loggedin';
2212
+ await el.updateComplete;
2213
+ await el.initialSearchComplete;
2214
+
2215
+ const blurringCheck = el.shadowRoot?.querySelector(
2216
+ '#tile-blur-check',
2217
+ ) as HTMLInputElement;
2218
+ expect(blurringCheck).to.exist;
2219
+
2220
+ blurringCheck.dispatchEvent(new PointerEvent('click'));
2221
+ await el.updateComplete;
2222
+
2223
+ const infiniteScroller = el.shadowRoot?.querySelector(
2224
+ 'infinite-scroller',
2225
+ ) as InfiniteScroller;
2226
+ const firstTile = infiniteScroller?.shadowRoot?.querySelector(
2227
+ 'tile-dispatcher',
2228
+ ) as TileDispatcher;
2229
+ expect(firstTile.suppressBlurring).to.be.true;
2230
+ });
2231
+
2148
2232
  it('applies loans tab properties to sort bar', async () => {
2149
2233
  const searchService = new MockSearchService();
2150
2234
  const el = await fixture<CollectionBrowser>(
@@ -220,4 +220,28 @@ describe('Image block component', () => {
220
220
  ) as TextOverlay;
221
221
  expect(textOverlay).not.to.exist;
222
222
  });
223
+
224
+ it('should render no overlay if blurring is suppressed', async () => {
225
+ const el = await fixture<ImageBlock>(html`
226
+ <image-block
227
+ .model=${{
228
+ loginRequired: true,
229
+ contentWarning: true,
230
+ identifier: 'goody',
231
+ }}
232
+ .baseImageUrl=${'https://archive.org'}
233
+ .isCompactTile=${false}
234
+ .isListTile=${false}
235
+ .viewSize=${'desktop'}
236
+ .loggedIn=${true}
237
+ suppressBlurring
238
+ >
239
+ </image-block>
240
+ `);
241
+
242
+ const textOverlay = el.shadowRoot?.querySelector(
243
+ 'text-overlay',
244
+ ) as TextOverlay;
245
+ expect(textOverlay).not.to.exist;
246
+ });
223
247
  });
@@ -5,6 +5,7 @@ import { TileModel } from '../src/models';
5
5
  import type { ItemImage } from '../src/tiles/item-image';
6
6
 
7
7
  import '../src/tiles/item-image';
8
+ import { MediaType } from '@internetarchive/field-parsers';
8
9
 
9
10
  const baseImageUrl = 'https://archive.org';
10
11
  const testBookModel: TileModel = new TileModel({});
@@ -131,4 +132,89 @@ describe('ItemImage component', () => {
131
132
  expect(img).to.exist;
132
133
  expect(img?.getAttribute('src')).not.to.exist;
133
134
  });
135
+
136
+ it('should blur image if login required flag set', async () => {
137
+ const model = new TileModel({ identifier: 'foo' });
138
+ model.loginRequired = true;
139
+
140
+ const el = await fixture<ItemImage>(html`
141
+ <item-image
142
+ .isListTile=${false}
143
+ .isCompactTile=${false}
144
+ .model=${model}
145
+ .baseImageUrl=${baseImageUrl}
146
+ >
147
+ </item-image>
148
+ `);
149
+
150
+ const dropShadow = el.shadowRoot?.querySelector('.drop-shadow');
151
+ expect(dropShadow).to.exist;
152
+
153
+ const imgClasses = dropShadow?.querySelector('img')?.classList;
154
+ expect(imgClasses?.contains('blur')).to.be.true;
155
+ });
156
+
157
+ it('should blur image if content warning flag set', async () => {
158
+ const model = new TileModel({ identifier: 'foo' });
159
+ model.contentWarning = true;
160
+
161
+ const el = await fixture<ItemImage>(html`
162
+ <item-image
163
+ .isListTile=${false}
164
+ .isCompactTile=${false}
165
+ .model=${model}
166
+ .baseImageUrl=${baseImageUrl}
167
+ >
168
+ </item-image>
169
+ `);
170
+
171
+ const dropShadow = el.shadowRoot?.querySelector('.drop-shadow');
172
+ expect(dropShadow).to.exist;
173
+
174
+ const imgClasses = dropShadow?.querySelector('img')?.classList;
175
+ expect(imgClasses?.contains('blur')).to.be.true;
176
+ });
177
+
178
+ it('should not blur image if no login required nor content warning', async () => {
179
+ const model = new TileModel({ identifier: 'foo' });
180
+
181
+ const el = await fixture<ItemImage>(html`
182
+ <item-image
183
+ .isListTile=${false}
184
+ .isCompactTile=${false}
185
+ .model=${model}
186
+ .baseImageUrl=${baseImageUrl}
187
+ >
188
+ </item-image>
189
+ `);
190
+
191
+ const dropShadow = el.shadowRoot?.querySelector('.drop-shadow');
192
+ expect(dropShadow).to.exist;
193
+
194
+ const imgClasses = dropShadow?.querySelector('img')?.classList;
195
+ expect(imgClasses?.contains('blur')).to.be.false;
196
+ });
197
+
198
+ it('should not blur image if blurring is suppressed, regardless of content flags', async () => {
199
+ const model = new TileModel({ identifier: 'foo' });
200
+ model.loginRequired = true;
201
+ model.contentWarning = true;
202
+
203
+ const el = await fixture<ItemImage>(html`
204
+ <item-image
205
+ .isListTile=${false}
206
+ .isCompactTile=${false}
207
+ .model=${model}
208
+ .baseImageUrl=${baseImageUrl}
209
+ suppressBlurring
210
+ >
211
+ </item-image>
212
+ `);
213
+
214
+ const dropShadow = el.shadowRoot?.querySelector('.drop-shadow');
215
+ expect(dropShadow).to.exist;
216
+
217
+ const imgClasses = dropShadow?.querySelector('img')?.classList;
218
+ expect(imgClasses?.contains('blur')).to.be.false;
219
+ });
134
220
  });