@internetarchive/collection-browser 1.14.17-alpha.4 → 1.14.17-alpha.41

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 (56) hide show
  1. package/dist/index.d.ts +3 -2
  2. package/dist/index.js +3 -2
  3. package/dist/index.js.map +1 -1
  4. package/dist/src/collection-browser.d.ts +6 -13
  5. package/dist/src/collection-browser.js +56 -30
  6. package/dist/src/collection-browser.js.map +1 -1
  7. package/dist/src/data-source/collection-browser-data-source-interface.d.ts +194 -0
  8. package/dist/src/data-source/collection-browser-data-source-interface.js +2 -0
  9. package/dist/src/data-source/collection-browser-data-source-interface.js.map +1 -0
  10. package/dist/src/data-source/collection-browser-data-source.d.ts +62 -174
  11. package/dist/src/data-source/collection-browser-data-source.js +185 -64
  12. package/dist/src/data-source/collection-browser-data-source.js.map +1 -1
  13. package/dist/src/data-source/collection-browser-query-state.d.ts +42 -0
  14. package/dist/src/data-source/collection-browser-query-state.js +2 -0
  15. package/dist/src/data-source/collection-browser-query-state.js.map +1 -0
  16. package/dist/src/data-source/models.d.ts +1 -39
  17. package/dist/src/data-source/models.js.map +1 -1
  18. package/dist/src/models.d.ts +3 -2
  19. package/dist/src/models.js +24 -22
  20. package/dist/src/models.js.map +1 -1
  21. package/dist/src/sort-filter-bar/sort-filter-bar.js +1 -0
  22. package/dist/src/sort-filter-bar/sort-filter-bar.js.map +1 -1
  23. package/dist/src/tiles/grid/item-tile.d.ts +1 -0
  24. package/dist/src/tiles/grid/item-tile.js +28 -1
  25. package/dist/src/tiles/grid/item-tile.js.map +1 -1
  26. package/dist/src/tiles/grid/tile-stats.js +13 -8
  27. package/dist/src/tiles/grid/tile-stats.js.map +1 -1
  28. package/dist/src/tiles/item-image.d.ts +3 -0
  29. package/dist/src/tiles/item-image.js +30 -2
  30. package/dist/src/tiles/item-image.js.map +1 -1
  31. package/dist/src/tiles/list/tile-list.d.ts +1 -0
  32. package/dist/src/tiles/list/tile-list.js +30 -1
  33. package/dist/src/tiles/list/tile-list.js.map +1 -1
  34. package/dist/src/tiles/tile-dispatcher.js +3 -2
  35. package/dist/src/tiles/tile-dispatcher.js.map +1 -1
  36. package/dist/src/tiles/tile-display-value-provider.d.ts +5 -1
  37. package/dist/src/tiles/tile-display-value-provider.js +15 -1
  38. package/dist/src/tiles/tile-display-value-provider.js.map +1 -1
  39. package/dist/test/collection-browser.test.js +2 -6
  40. package/dist/test/collection-browser.test.js.map +1 -1
  41. package/index.ts +3 -5
  42. package/package.json +2 -2
  43. package/src/collection-browser.ts +70 -49
  44. package/src/data-source/collection-browser-data-source-interface.ts +245 -0
  45. package/src/data-source/collection-browser-data-source.ts +237 -281
  46. package/src/data-source/collection-browser-query-state.ts +53 -0
  47. package/src/data-source/models.ts +0 -47
  48. package/src/models.ts +7 -3
  49. package/src/sort-filter-bar/sort-filter-bar.ts +1 -0
  50. package/src/tiles/grid/item-tile.ts +36 -1
  51. package/src/tiles/grid/tile-stats.ts +12 -7
  52. package/src/tiles/item-image.ts +28 -0
  53. package/src/tiles/list/tile-list.ts +37 -1
  54. package/src/tiles/tile-dispatcher.ts +2 -1
  55. package/src/tiles/tile-display-value-provider.ts +19 -1
  56. package/test/collection-browser.test.ts +2 -8
@@ -0,0 +1,53 @@
1
+ import type {
2
+ CollectionExtraInfo,
3
+ PageElementName,
4
+ SearchServiceInterface,
5
+ SearchType,
6
+ SortDirection,
7
+ SortParam,
8
+ } from '@internetarchive/search-service';
9
+ import type { SelectedFacets, SortField } from '../models';
10
+ import type { CollectionBrowserDataSourceInterface } from './collection-browser-data-source-interface';
11
+
12
+ /**
13
+ * Properties of collection browser that affect the overall search query
14
+ */
15
+ export interface CollectionBrowserQueryState {
16
+ baseQuery?: string;
17
+ withinCollection?: string;
18
+ withinProfile?: string;
19
+ profileElement?: PageElementName;
20
+ searchType: SearchType;
21
+ selectedFacets?: SelectedFacets;
22
+ minSelectedDate?: string;
23
+ maxSelectedDate?: string;
24
+ selectedTitleFilter: string | null;
25
+ selectedCreatorFilter: string | null;
26
+ selectedSort?: SortField;
27
+ sortDirection: SortDirection | null;
28
+ }
29
+
30
+ /**
31
+ * Interface representing search-related state and operations required by the
32
+ * data source on its host component.
33
+ */
34
+ export interface CollectionBrowserSearchInterface
35
+ extends CollectionBrowserQueryState {
36
+ searchService?: SearchServiceInterface;
37
+ readonly sortParam: SortParam | null;
38
+ readonly suppressFacets?: boolean;
39
+ readonly initialPageNumber: number;
40
+ readonly currentVisiblePageNumbers: number[];
41
+ readonly clearResultsOnEmptyQuery?: boolean;
42
+ readonly dataSource?: CollectionBrowserDataSourceInterface;
43
+
44
+ getSessionId(): Promise<string>;
45
+ setSearchResultsLoading(loading: boolean): void;
46
+ setFacetsLoading(loading: boolean): void;
47
+ setTotalResultCount(count: number): void;
48
+ setTileCount(count: number): void;
49
+ applyDefaultCollectionSort(collectionInfo?: CollectionExtraInfo): void;
50
+ emitEmptyResults(): void;
51
+ emitQueryStateChanged(): void;
52
+ refreshVisibleResults(): void;
53
+ }
@@ -1,13 +1,7 @@
1
1
  import type {
2
- CollectionExtraInfo,
3
2
  PageElementName,
4
3
  PageType,
5
- SearchServiceInterface,
6
- SearchType,
7
- SortDirection,
8
- SortParam,
9
4
  } from '@internetarchive/search-service';
10
- import type { SelectedFacets, SortField } from '../models';
11
5
 
12
6
  /**
13
7
  * A Map from collection identifiers to their corresponding collection titles.
@@ -33,44 +27,3 @@ export type PageSpecifierParams = {
33
27
  */
34
28
  pageElements?: PageElementName[];
35
29
  };
36
-
37
- /**
38
- * Properties of collection browser that affect the overall search query
39
- */
40
- export interface CollectionBrowserQueryState {
41
- baseQuery?: string;
42
- withinCollection?: string;
43
- withinProfile?: string;
44
- profileElement?: PageElementName;
45
- searchType: SearchType;
46
- selectedFacets?: SelectedFacets;
47
- minSelectedDate?: string;
48
- maxSelectedDate?: string;
49
- selectedTitleFilter: string | null;
50
- selectedCreatorFilter: string | null;
51
- selectedSort?: SortField;
52
- sortDirection: SortDirection | null;
53
- }
54
-
55
- /**
56
- * Interface representing search-related state and operations required by the
57
- * data source on its host component.
58
- */
59
- export interface CollectionBrowserSearchInterface
60
- extends CollectionBrowserQueryState {
61
- searchService?: SearchServiceInterface;
62
- queryErrorMessage?: string;
63
- readonly sortParam: SortParam | null;
64
- readonly suppressFacets?: boolean;
65
- readonly initialPageNumber: number;
66
- readonly currentVisiblePageNumbers: number[];
67
-
68
- getSessionId(): Promise<string>;
69
- setSearchResultsLoading(loading: boolean): void;
70
- setFacetsLoading(loading: boolean): void;
71
- setTotalResultCount(count: number): void;
72
- setTileCount(count: number): void;
73
- applyDefaultCollectionSort(collectionInfo?: CollectionExtraInfo): void;
74
- emitEmptyResults(): void;
75
- refreshVisibleResults(): void;
76
- }
package/src/models.ts CHANGED
@@ -21,6 +21,8 @@ interface TileFlags {
21
21
  export class TileModel {
22
22
  averageRating?: number;
23
23
 
24
+ captureDates?: Date[]; // List of capture dates for a URL, used on profile Web Archives tiles
25
+
24
26
  checked: boolean; // Whether this tile is currently checked for item management functions
25
27
 
26
28
  collectionIdentifier?: string;
@@ -71,7 +73,7 @@ export class TileModel {
71
73
 
72
74
  title: string;
73
75
 
74
- viewCount: number;
76
+ viewCount?: number;
75
77
 
76
78
  volume?: string;
77
79
 
@@ -85,6 +87,7 @@ export class TileModel {
85
87
  const flags = this.getFlags(result);
86
88
 
87
89
  this.averageRating = result.avg_rating?.value;
90
+ this.captureDates = result.capture_dates?.values;
88
91
  this.checked = false;
89
92
  this.collections = result.collection?.values ?? [];
90
93
  this.collectionFilesCount = result.collection_files_count?.value ?? 0;
@@ -108,18 +111,19 @@ export class TileModel {
108
111
  this.subjects = result.subject?.values ?? [];
109
112
  this.title = result.title?.value ?? '';
110
113
  this.volume = result.volume?.value;
111
- this.viewCount = result.downloads?.value ?? 0;
114
+ this.viewCount = result.downloads?.value;
112
115
  this.weeklyViewCount = result.week?.value;
113
116
  this.loginRequired = flags.loginRequired;
114
117
  this.contentWarning = flags.contentWarning;
115
118
  }
116
119
 
117
120
  /**
118
- * Clones the contents of this TileModel onto a new instance
121
+ * Copies the contents of this TileModel onto a new instance
119
122
  */
120
123
  clone(): TileModel {
121
124
  const cloned = new TileModel({});
122
125
  cloned.averageRating = this.averageRating;
126
+ cloned.captureDates = this.captureDates;
123
127
  cloned.checked = this.checked;
124
128
  cloned.collections = this.collections;
125
129
  cloned.collectionFilesCount = this.collectionFilesCount;
@@ -1116,6 +1116,7 @@ export class SortFilterBar
1116
1116
  height: 100%;
1117
1117
  padding-left: 5px;
1118
1118
  font-size: 1.4rem;
1119
+ font-family: var(--ia-theme-base-font-family);
1119
1120
  line-height: 2;
1120
1121
  color: var(--ia-theme-primary-text-color, #2c2c2c);
1121
1122
  white-space: nowrap;
@@ -4,6 +4,7 @@ import { customElement, property } from 'lit/decorators.js';
4
4
  import { ifDefined } from 'lit/directives/if-defined.js';
5
5
  import { msg } from '@lit/localize';
6
6
 
7
+ import { map } from 'lit/directives/map.js';
7
8
  import { DateFormat, formatDate } from '../../utils/format-date';
8
9
  import { isFirstMillisecondOfUTCYear } from '../../utils/local-date-from-utc';
9
10
  import { BaseTileComponent } from '../base-tile-component';
@@ -57,7 +58,7 @@ export class ItemTile extends BaseTileComponent {
57
58
  ${this.isSortedByDate
58
59
  ? this.sortedDateInfoTemplate
59
60
  : this.creatorTemplate}
60
- ${this.textSnippetsTemplate}
61
+ ${this.webArchivesCaptureDatesTemplate} ${this.textSnippetsTemplate}
61
62
  </div>
62
63
 
63
64
  <tile-stats
@@ -172,6 +173,26 @@ export class ItemTile extends BaseTileComponent {
172
173
  `;
173
174
  }
174
175
 
176
+ private get webArchivesCaptureDatesTemplate():
177
+ | TemplateResult
178
+ | typeof nothing {
179
+ if (!this.model?.captureDates || !this.model?.title) return nothing;
180
+
181
+ return html`
182
+ <ul class="capture-dates">
183
+ ${map(
184
+ this.model.captureDates,
185
+ date => html`<li>
186
+ ${this.displayValueProvider.webArchivesCaptureLink(
187
+ this.model!.title,
188
+ date
189
+ )}
190
+ </li>`
191
+ )}
192
+ </ul>
193
+ `;
194
+ }
195
+
175
196
  private get isSortedByDate(): boolean {
176
197
  return ['date', 'reviewdate', 'addeddate', 'publicdate'].includes(
177
198
  this.sortParam?.field as string
@@ -200,10 +221,24 @@ export class ItemTile extends BaseTileComponent {
200
221
  return [
201
222
  baseTileStyles,
202
223
  css`
224
+ a:link {
225
+ text-decoration: none;
226
+ color: var(--ia-theme-link-color, #4b64ff);
227
+ }
228
+ a:hover {
229
+ text-decoration: underline;
230
+ }
231
+
203
232
  .container {
204
233
  border: 1px solid ${tileBorderColor};
205
234
  }
206
235
 
236
+ .capture-dates {
237
+ margin: 0;
238
+ padding: 0 5px;
239
+ list-style-type: none;
240
+ }
241
+
207
242
  text-snippet-block {
208
243
  --containerLeftMargin: 5px;
209
244
  --containerTopMargin: 5px;
@@ -1,6 +1,7 @@
1
1
  import { css, CSSResultGroup, html, LitElement } from 'lit';
2
2
  import { customElement, property } from 'lit/decorators.js';
3
3
 
4
+ import { msg } from '@lit/localize';
4
5
  import { favoriteFilledIcon } from '../../assets/img/icons/favorite-filled';
5
6
  import { reviewsIcon } from '../../assets/img/icons/reviews';
6
7
  import { uploadIcon } from '../../assets/img/icons/upload';
@@ -33,8 +34,8 @@ export class TileStats extends LitElement {
33
34
 
34
35
  const uploadsOrViewsTitle =
35
36
  this.mediatype === 'account'
36
- ? `${this.itemCount} uploads`
37
- : `${this.viewCount} ${this.viewLabel ?? 'all-time views'}`;
37
+ ? `${this.itemCount ?? 0} uploads`
38
+ : `${this.viewCount ?? 0} ${this.viewLabel ?? 'all-time views'}`;
38
39
 
39
40
  return html`
40
41
  <div class="item-stats">
@@ -43,17 +44,21 @@ export class TileStats extends LitElement {
43
44
  </p>
44
45
  <ul id="stats-row">
45
46
  <li class="col">
46
- <p class="sr-only">Mediatype:</p>
47
+ <p class="sr-only">${msg('Mediatype:')}</p>
47
48
  <mediatype-icon .mediatype=${this.mediatype}></mediatype-icon>
48
49
  </li>
49
50
  <li class="col" title="${uploadsOrViewsTitle}">
50
51
  ${this.mediatype === 'account' ? uploadIcon : viewsIcon}
51
52
  <p class="status-text">
52
53
  <span class="sr-only">
53
- ${this.mediatype === 'account' ? 'Uploads:' : 'Views:'}
54
+ ${this.mediatype === 'account'
55
+ ? msg('Uploads:')
56
+ : msg('Views:')}
54
57
  </span>
55
58
  ${formatCount(
56
- this.mediatype === 'account' ? this.itemCount : this.viewCount,
59
+ this.mediatype === 'account'
60
+ ? this.itemCount ?? 0
61
+ : this.viewCount ?? 0,
57
62
  'short',
58
63
  'short'
59
64
  )}
@@ -62,14 +67,14 @@ export class TileStats extends LitElement {
62
67
  <li class="col" title="${formattedFavCount} favorites">
63
68
  ${favoriteFilledIcon}
64
69
  <p class="status-text">
65
- <span class="sr-only">Favorites:</span>
70
+ <span class="sr-only">${msg('Favorites:')}</span>
66
71
  ${formattedFavCount}
67
72
  </p>
68
73
  </li>
69
74
  <li class="col reviews" title="${formattedReviewCount} reviews">
70
75
  ${reviewsIcon}
71
76
  <p class="status-text">
72
- <span class="sr-only">Reviews:</span>
77
+ <span class="sr-only">${msg('Reviews:')}</span>
73
78
  ${formattedReviewCount}
74
79
  </p>
75
80
  </li>
@@ -24,6 +24,8 @@ export class ItemImage extends LitElement {
24
24
 
25
25
  @state() private isWaveform = false;
26
26
 
27
+ @state() private isNotFound = false;
28
+
27
29
  @query('img') private baseImage!: HTMLImageElement;
28
30
 
29
31
  render() {
@@ -43,6 +45,7 @@ export class ItemImage extends LitElement {
43
45
  src="${this.imageSrc}"
44
46
  alt=""
45
47
  @load=${this.onLoad}
48
+ @error=${this.onError}
46
49
  />
47
50
  `;
48
51
  }
@@ -51,12 +54,33 @@ export class ItemImage extends LitElement {
51
54
  * Helpers
52
55
  */
53
56
  private get imageSrc() {
57
+ if (this.isNotFound) return this.notFoundSrc;
58
+
59
+ // Use the correct image for web capture tiles, if possible
60
+ if (this.model?.captureDates && this.model.identifier) {
61
+ try {
62
+ const url = new URL(this.model.identifier);
63
+ const domain = encodeURIComponent(url.hostname);
64
+ return this.baseImageUrl
65
+ ? `https://web.archive.org/thumb/${domain}?generate=1`
66
+ : nothing;
67
+ } catch (err) {
68
+ return `${this.baseImageUrl}/images/notfound.png`;
69
+ }
70
+ }
71
+
54
72
  // Don't try to load invalid image URLs
55
73
  return this.baseImageUrl && this.model?.identifier
56
74
  ? `${this.baseImageUrl}/services/img/${this.model.identifier}`
57
75
  : nothing;
58
76
  }
59
77
 
78
+ private get notFoundSrc() {
79
+ return this.baseImageUrl
80
+ ? `${this.baseImageUrl}/images/notfound.png`
81
+ : nothing;
82
+ }
83
+
60
84
  private get hashBasedGradient() {
61
85
  if (!this.model?.identifier) {
62
86
  return 'waveform-grad0';
@@ -120,6 +144,10 @@ export class ItemImage extends LitElement {
120
144
  }
121
145
  }
122
146
 
147
+ private onError() {
148
+ this.isNotFound = true;
149
+ }
150
+
123
151
  /**
124
152
  * CSS
125
153
  */
@@ -107,6 +107,7 @@ export class TileList extends BaseTileComponent {
107
107
  ${this.itemLineTemplate} ${this.creatorTemplate}
108
108
  <div id="dates-line">
109
109
  ${this.datePublishedTemplate} ${this.dateSortByTemplate}
110
+ ${this.webArchivesCaptureDatesTemplate}
110
111
  </div>
111
112
  <div id="views-line">
112
113
  ${this.viewsTemplate} ${this.ratingTemplate} ${this.reviewsTemplate}
@@ -238,6 +239,7 @@ export class TileList extends BaseTileComponent {
238
239
  this.sortParam?.field === 'week'
239
240
  ? this.model?.weeklyViewCount // weekly views
240
241
  : this.model?.viewCount; // all-time views
242
+ if (viewCount == null) return nothing;
241
243
 
242
244
  // when its a search-tile, we don't have any stats to show
243
245
  if (this.model?.mediatype === 'search') {
@@ -245,7 +247,7 @@ export class TileList extends BaseTileComponent {
245
247
  }
246
248
 
247
249
  return this.metadataTemplate(
248
- `${formatCount(viewCount ?? 0, this.formatSize)}`,
250
+ `${formatCount(viewCount, this.formatSize)}`,
249
251
  msg('Views')
250
252
  );
251
253
  }
@@ -309,6 +311,26 @@ export class TileList extends BaseTileComponent {
309
311
  return !!this.model?.snippets?.length;
310
312
  }
311
313
 
314
+ private get webArchivesCaptureDatesTemplate():
315
+ | TemplateResult
316
+ | typeof nothing {
317
+ if (!this.model?.captureDates || !this.model?.title) return nothing;
318
+
319
+ return html`
320
+ <ul class="capture-dates">
321
+ ${map(
322
+ this.model.captureDates,
323
+ date => html`<li>
324
+ ${this.displayValueProvider.webArchivesCaptureLink(
325
+ this.model!.title,
326
+ date
327
+ )}
328
+ </li>`
329
+ )}
330
+ </ul>
331
+ `;
332
+ }
333
+
312
334
  // Utility functions
313
335
  // eslint-disable-next-line default-param-last
314
336
  private metadataTemplate(text: any, label = '', id?: string) {
@@ -624,6 +646,20 @@ export class TileList extends BaseTileComponent {
624
646
  #views-line {
625
647
  flex-wrap: wrap;
626
648
  }
649
+
650
+ .capture-dates {
651
+ margin: 0;
652
+ padding: 0;
653
+ list-style-type: none;
654
+ }
655
+
656
+ .capture-dates a:link {
657
+ text-decoration: none;
658
+ color: var(--ia-theme-link-color, #4b64ff);
659
+ }
660
+ .capture-dates a:hover {
661
+ text-decoration: underline;
662
+ }
627
663
  `;
628
664
  }
629
665
  }
@@ -184,7 +184,8 @@ export class TileDispatcher
184
184
  this.enableHoverPane &&
185
185
  !!this.tileDisplayMode &&
186
186
  TileDispatcher.HOVER_PANE_DISPLAY_MODES[this.tileDisplayMode] &&
187
- this.model?.mediatype !== 'search' // don't show hover panes on search tiles
187
+ this.model?.mediatype !== 'search' && // don't show hover panes on search tiles
188
+ !this.model?.captureDates // don't show hover panes on web archive tiles
188
189
  );
189
190
  }
190
191
 
@@ -1,7 +1,8 @@
1
- import { nothing } from 'lit';
1
+ import { TemplateResult, html, nothing } from 'lit';
2
2
  import { msg, str } from '@lit/localize';
3
3
  import type { SortParam } from '@internetarchive/search-service';
4
4
  import type { TileModel } from '../models';
5
+ import { formatDate } from '../utils/format-date';
5
6
 
6
7
  /**
7
8
  * A class encapsulating shared logic for converting model values into display values
@@ -103,4 +104,21 @@ export class TileDisplayValueProvider {
103
104
  const basePath = isCollection ? this.collectionPagePath : '/details/';
104
105
  return `${this.baseNavigationUrl}${basePath}${identifier}`;
105
106
  }
107
+
108
+ /**
109
+ * Produces a template for a link to a single web capture of the given URL and date
110
+ */
111
+ webArchivesCaptureLink(url: string, date: Date): TemplateResult {
112
+ // Convert the date into the format used to identify wayback captures (e.g., '20150102124550')
113
+ const captureDateStr = date
114
+ .toISOString()
115
+ .replace(/[TZ:-]/g, '')
116
+ .replace(/\..*/, '');
117
+ const captureHref = `https://web.archive.org/web/${captureDateStr}/${encodeURIComponent(
118
+ url
119
+ )}`;
120
+ const captureText = formatDate(date, 'long');
121
+
122
+ return html` <a href=${captureHref}> ${captureText} </a> `;
123
+ }
106
124
  }
@@ -917,7 +917,7 @@ describe('Collection Browser', () => {
917
917
  el.sortDirection = 'asc';
918
918
  el.selectedCreatorFilter = 'X';
919
919
  await el.updateComplete;
920
- await el.initialSearchComplete;
920
+ await nextTick();
921
921
 
922
922
  expect(searchService.searchParams?.query).to.equal('first-creator');
923
923
  expect(searchService.searchParams?.filters?.firstCreator?.X).to.equal(
@@ -926,7 +926,7 @@ describe('Collection Browser', () => {
926
926
 
927
927
  el.baseQuery = 'collection:foo';
928
928
  await el.updateComplete;
929
- await el.initialSearchComplete;
929
+ await nextTick();
930
930
 
931
931
  expect(searchService.searchParams?.query).to.equal('collection:foo');
932
932
  expect(searchService.searchParams?.filters?.firstCreator).not.to.exist;
@@ -1446,9 +1446,6 @@ describe('Collection Browser', () => {
1446
1446
  </collection-browser>`
1447
1447
  );
1448
1448
 
1449
- await el.initialSearchComplete;
1450
- await el.updateComplete;
1451
-
1452
1449
  el.baseQuery = 'bar';
1453
1450
  await el.updateComplete;
1454
1451
 
@@ -1484,9 +1481,6 @@ describe('Collection Browser', () => {
1484
1481
  </collection-browser>`
1485
1482
  );
1486
1483
 
1487
- await el.initialSearchComplete;
1488
- await el.updateComplete;
1489
-
1490
1484
  el.withinCollection = 'bar';
1491
1485
  await el.updateComplete;
1492
1486