@schukai/monster 4.136.19 → 4.136.21

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/package.json CHANGED
@@ -1 +1 @@
1
- {"author":"Volker Schukai","dependencies":{"@floating-ui/dom":"^1.7.6"},"description":"Monster is a simple library for creating fast, robust and lightweight websites.","homepage":"https://monsterjs.org/","keywords":["framework","web","dom","css","sass","mobile-first","app","front-end","templates","schukai","core","shopcloud","alvine","monster","buildmap","stack","observer","observable","uuid","node","nodelist","css-in-js","logger","log","theme"],"license":"AGPL 3.0","main":"source/monster.mjs","module":"source/monster.mjs","name":"@schukai/monster","repository":{"type":"git","url":"https://gitlab.schukai.com/oss/libraries/javascript/monster.git"},"type":"module","version":"4.136.19"}
1
+ {"author":"Volker Schukai","dependencies":{"@floating-ui/dom":"^1.7.6"},"description":"Monster is a simple library for creating fast, robust and lightweight websites.","homepage":"https://monsterjs.org/","keywords":["framework","web","dom","css","sass","mobile-first","app","front-end","templates","schukai","core","shopcloud","alvine","monster","buildmap","stack","observer","observable","uuid","node","nodelist","css-in-js","logger","log","theme"],"license":"AGPL 3.0","main":"source/monster.mjs","module":"source/monster.mjs","name":"@schukai/monster","repository":{"type":"git","url":"https://gitlab.schukai.com/oss/libraries/javascript/monster.git"},"type":"module","version":"4.136.21"}
@@ -205,10 +205,26 @@ class Pagination extends CustomElement {
205
205
  return;
206
206
  }
207
207
 
208
+ const previousCurrentPage = Number.parseInt(
209
+ this.getOption("currentPage"),
210
+ 10,
211
+ );
212
+ const previousTotalPages = Number.parseInt(this.getOption("pages"), 10);
213
+ const stateUnchanged =
214
+ Number.isFinite(previousCurrentPage) &&
215
+ Number.isFinite(previousTotalPages) &&
216
+ previousCurrentPage === currentPage &&
217
+ previousTotalPages === totalPages;
218
+
208
219
  // 1. Update the component's internal options with the new values.
209
220
  this.setOption("currentPage", currentPage);
210
221
  this.setOption("pages", totalPages);
211
222
 
223
+ if (stateUnchanged) {
224
+ this.refreshLayout({ schedule: true });
225
+ return;
226
+ }
227
+
212
228
  // 2. Call the existing buildPagination function to recalculate the links.
213
229
  const pagination = buildPagination.call(
214
230
  this,
@@ -227,20 +243,35 @@ class Pagination extends CustomElement {
227
243
  // 4. Set the 'pagination' option, which will trigger the component to re-render.
228
244
  this.setOption("pagination", pagination);
229
245
 
230
- if (isAdaptivePagination.call(this)) {
231
- const list = this.shadowRoot?.querySelector(".pagination-list");
232
- if (list) {
233
- list.setAttribute("data-monster-adaptive-ready", "false");
234
- }
235
- schedulePaginationLayoutUpdate.call(this);
236
- } else {
237
- const list = this.shadowRoot?.querySelector(".pagination-list");
238
- if (list) {
239
- list.setAttribute("data-monster-adaptive-ready", "true");
246
+ this.refreshLayout({ schedule: true });
247
+
248
+ syncPaginationStateToDom.call(this, pagination);
249
+ }
250
+
251
+ refreshLayout({ schedule = false } = {}) {
252
+ if (schedule === true) {
253
+ if (isAdaptivePagination.call(this)) {
254
+ schedulePaginationLayoutUpdate.call(this);
255
+ } else {
256
+ const list = this.shadowRoot?.querySelector(".pagination-list");
257
+ if (list) {
258
+ list.setAttribute("data-monster-adaptive-ready", "true");
259
+ }
240
260
  }
261
+ return this;
241
262
  }
242
263
 
243
- syncPaginationStateToDom.call(this, pagination);
264
+ if (isAdaptivePagination.call(this)) {
265
+ applyPaginationLayout.call(this);
266
+ return this;
267
+ }
268
+
269
+ const list = this.shadowRoot?.querySelector(".pagination-list");
270
+ if (list) {
271
+ list.setAttribute("data-monster-adaptive-ready", "true");
272
+ }
273
+
274
+ return this;
244
275
  }
245
276
  /**
246
277
  *
@@ -677,14 +708,7 @@ function handleDataSourceChanges() {
677
708
 
678
709
  this.setOption("pagination", pagination);
679
710
 
680
- if (isAdaptivePagination.call(this)) {
681
- schedulePaginationLayoutUpdate.call(this);
682
- } else {
683
- const list = this.shadowRoot?.querySelector(".pagination-list");
684
- if (list) {
685
- list.setAttribute("data-monster-adaptive-ready", "true");
686
- }
687
- }
711
+ this.refreshLayout({ schedule: isAdaptivePagination.call(this) });
688
712
  }
689
713
 
690
714
  /**
@@ -138,6 +138,12 @@ const clearOptionEventHandler = Symbol("clearOptionEventHandler");
138
138
  */
139
139
  const resizeObserverSymbol = Symbol("resizeObserver");
140
140
  const resizeObserverFrameSymbol = Symbol("resizeObserverFrame");
141
+ const layoutCycleFrameSymbol = Symbol("layoutCycleFrame");
142
+ const layoutCycleTokenSymbol = Symbol("layoutCycleToken");
143
+ const layoutCycleModeSymbol = Symbol("layoutCycleMode");
144
+ const layoutCyclePendingFlagsSymbol = Symbol("layoutCyclePendingFlags");
145
+ const layoutCyclePendingPrioritySymbol = Symbol("layoutCyclePendingPriority");
146
+ const layoutCycleRunningSymbol = Symbol("layoutCycleRunning");
141
147
  const visualViewportResizeHandlerSymbol = Symbol("visualViewportResizeHandler");
142
148
  const visualViewportScrollHandlerSymbol = Symbol("visualViewportScrollHandler");
143
149
  const visibilityChangeHandlerSymbol = Symbol("visibilityChangeHandler");
@@ -307,6 +313,19 @@ const lookupInProgressSymbol = Symbol("lookupInProgress");
307
313
  const unresolvedSelectionValuesSymbol = Symbol("unresolvedSelectionValues");
308
314
  const fetchRequestVersionSymbol = Symbol("fetchRequestVersion");
309
315
  const remoteInfoRequestSymbol = Symbol("remoteInfoRequest");
316
+ const remoteInfoStableMessageSymbol = Symbol("remoteInfoStableMessage");
317
+
318
+ const SELECT_LAYOUT_PRIORITY_PASSIVE = 1;
319
+ const SELECT_LAYOUT_PRIORITY_INTERACTIVE = 2;
320
+ const SELECT_LAYOUT_PRIORITY_CRITICAL = 3;
321
+
322
+ const SELECT_LAYOUT_REASON_OPTION_STATE = 1 << 0;
323
+ const SELECT_LAYOUT_REASON_PAGINATION = 1 << 1;
324
+ const SELECT_LAYOUT_REASON_POSITION = 1 << 2;
325
+ const SELECT_LAYOUT_REASON_ALL =
326
+ SELECT_LAYOUT_REASON_OPTION_STATE |
327
+ SELECT_LAYOUT_REASON_PAGINATION |
328
+ SELECT_LAYOUT_REASON_POSITION;
310
329
 
311
330
  /**
312
331
  * @private
@@ -709,19 +728,21 @@ class Select extends CustomControl {
709
728
  }
710
729
 
711
730
  this.setOption("messages.selected", "");
712
- this.setOption("messages.total", "");
731
+ setRemoteInfoText.call(this, "");
713
732
  this.setOption("messages.summary", "");
714
733
  this.setOption("total", null);
715
734
  resetPaginationState.call(this);
716
735
 
717
- resetErrorAttribute(this);
736
+ resetErrorAttribute(this);
718
737
 
719
- this[lazyLoadDoneSymbol] = false;
738
+ this[lazyLoadDoneSymbol] = false;
720
739
 
721
- checkOptionState.call(this);
722
- calcAndSetOptionsDimension.call(this);
723
- updatePopper.call(this);
724
- })
740
+ scheduleSelectLayoutCycle.call(
741
+ this,
742
+ SELECT_LAYOUT_PRIORITY_CRITICAL,
743
+ SELECT_LAYOUT_REASON_ALL,
744
+ );
745
+ })
725
746
  .catch((e) => {
726
747
  addErrorAttribute(this, e);
727
748
  });
@@ -1102,6 +1123,44 @@ function processAndApplyRemoteInfoTotal(data) {
1102
1123
  }
1103
1124
  }
1104
1125
 
1126
+ /**
1127
+ * @private
1128
+ * @param {string|null|undefined} message
1129
+ * @param {object} [options]
1130
+ * @param {boolean} [options.reserveSpace=false]
1131
+ * @returns {void}
1132
+ */
1133
+ function setRemoteInfoText(message, { reserveSpace = false } = {}) {
1134
+ const normalizedMessage =
1135
+ message === undefined || message === null ? "" : `${message}`;
1136
+ const previousStableMessage =
1137
+ typeof this[remoteInfoStableMessageSymbol] === "string"
1138
+ ? this[remoteInfoStableMessageSymbol]
1139
+ : "";
1140
+ const renderedMessage =
1141
+ reserveSpace === true &&
1142
+ normalizedMessage === "" &&
1143
+ previousStableMessage !== ""
1144
+ ? previousStableMessage
1145
+ : normalizedMessage;
1146
+
1147
+ this.setOption("messages.total", renderedMessage);
1148
+
1149
+ if (normalizedMessage !== "") {
1150
+ this[remoteInfoStableMessageSymbol] = normalizedMessage;
1151
+ } else if (reserveSpace !== true) {
1152
+ this[remoteInfoStableMessageSymbol] = "";
1153
+ }
1154
+
1155
+ if (this[remoteInfoElementSymbol] instanceof HTMLElement) {
1156
+ if (reserveSpace === true && renderedMessage !== "") {
1157
+ this[remoteInfoElementSymbol].style.visibility = "hidden";
1158
+ } else {
1159
+ this[remoteInfoElementSymbol].style.removeProperty("visibility");
1160
+ }
1161
+ }
1162
+ }
1163
+
1105
1164
  /**
1106
1165
  * @private
1107
1166
  * @returns {number}
@@ -1964,7 +2023,9 @@ function fetchIt(url, controlOptions) {
1964
2023
  classes.add("d-none");
1965
2024
  this.setOption("classes.noOptions", classes.toString());
1966
2025
  this.setOption("messages.emptyOptions", "");
1967
- this.setOption("messages.total", "");
2026
+ setRemoteInfoText.call(this, "", {
2027
+ reserveSpace: true,
2028
+ });
1968
2029
  }
1969
2030
 
1970
2031
  new Processing(10, () => {
@@ -2158,6 +2219,7 @@ function disconnectResizeObserver() {
2158
2219
  this[resizeObserverSymbol].disconnect();
2159
2220
  }
2160
2221
  cancelScheduledResizeObserverPopperUpdate.call(this);
2222
+ cancelScheduledSelectLayoutCycle.call(this);
2161
2223
 
2162
2224
  const viewport = getGlobal().visualViewport;
2163
2225
  if (
@@ -2217,7 +2279,11 @@ function scheduleResizeObserverPopperUpdate() {
2217
2279
 
2218
2280
  this[resizeObserverFrameSymbol] = schedule(() => {
2219
2281
  delete this[resizeObserverFrameSymbol];
2220
- updatePopper.call(this);
2282
+ scheduleSelectLayoutCycle.call(
2283
+ this,
2284
+ SELECT_LAYOUT_PRIORITY_CRITICAL,
2285
+ SELECT_LAYOUT_REASON_ALL,
2286
+ );
2221
2287
  });
2222
2288
  }
2223
2289
 
@@ -2236,6 +2302,155 @@ function cancelScheduledResizeObserverPopperUpdate() {
2236
2302
  delete this[resizeObserverFrameSymbol];
2237
2303
  }
2238
2304
 
2305
+ function applySelectLayoutState(layoutReasons = 0) {
2306
+ if (layoutReasons & SELECT_LAYOUT_REASON_OPTION_STATE) {
2307
+ checkOptionState.call(this);
2308
+ }
2309
+
2310
+ calcAndSetOptionsDimension.call(this);
2311
+
2312
+ if (layoutReasons & SELECT_LAYOUT_REASON_PAGINATION) {
2313
+ refreshSelectPaginationLayout.call(this);
2314
+ }
2315
+ }
2316
+
2317
+ function performSelectLayoutCycle(layoutReasons = SELECT_LAYOUT_REASON_ALL) {
2318
+ if (!isPositionedPopperOpen(this[popperElementSymbol])) {
2319
+ return Promise.resolve();
2320
+ }
2321
+
2322
+ if (this.getOption("disabled", false) === true) {
2323
+ return Promise.resolve();
2324
+ }
2325
+
2326
+ return new Processing(() => {
2327
+ applySelectLayoutState.call(this, layoutReasons);
2328
+ if (layoutReasons & SELECT_LAYOUT_REASON_POSITION) {
2329
+ return positionPopper.call(
2330
+ this,
2331
+ this[controlElementSymbol],
2332
+ this[popperElementSymbol],
2333
+ getSelectPopperPositionOptions.call(this),
2334
+ );
2335
+ }
2336
+ }).run();
2337
+ }
2338
+
2339
+ function cancelScheduledSelectLayoutCycle() {
2340
+ const globalObject = getGlobal();
2341
+ const frameId = this[layoutCycleFrameSymbol];
2342
+ if (typeof frameId === "number") {
2343
+ if (globalObject?.cancelAnimationFrame instanceof Function) {
2344
+ globalObject.cancelAnimationFrame(frameId);
2345
+ } else {
2346
+ globalObject.clearTimeout(frameId);
2347
+ }
2348
+ }
2349
+
2350
+ delete this[layoutCycleFrameSymbol];
2351
+ this[layoutCycleTokenSymbol] = (this[layoutCycleTokenSymbol] || 0) + 1;
2352
+ delete this[layoutCycleModeSymbol];
2353
+ }
2354
+
2355
+ function flushScheduledSelectLayoutCycle() {
2356
+ if (this[layoutCycleRunningSymbol] === true) {
2357
+ return;
2358
+ }
2359
+
2360
+ const layoutReasons =
2361
+ this[layoutCyclePendingFlagsSymbol] || SELECT_LAYOUT_REASON_ALL;
2362
+ const layoutPriority =
2363
+ this[layoutCyclePendingPrioritySymbol] || SELECT_LAYOUT_PRIORITY_INTERACTIVE;
2364
+
2365
+ delete this[layoutCyclePendingFlagsSymbol];
2366
+ delete this[layoutCyclePendingPrioritySymbol];
2367
+
2368
+ this[layoutCycleRunningSymbol] = true;
2369
+ performSelectLayoutCycle
2370
+ .call(this, layoutReasons)
2371
+ .catch((e) => {
2372
+ addErrorAttribute(this, e);
2373
+ })
2374
+ .finally(() => {
2375
+ delete this[layoutCycleRunningSymbol];
2376
+ if (this[layoutCyclePendingFlagsSymbol]) {
2377
+ scheduleSelectLayoutCycle.call(
2378
+ this,
2379
+ layoutPriority,
2380
+ this[layoutCyclePendingFlagsSymbol],
2381
+ );
2382
+ }
2383
+ });
2384
+ }
2385
+
2386
+ function scheduleSelectLayoutCycle(
2387
+ layoutPriority = SELECT_LAYOUT_PRIORITY_INTERACTIVE,
2388
+ layoutReasons = SELECT_LAYOUT_REASON_ALL,
2389
+ ) {
2390
+ this[layoutCyclePendingFlagsSymbol] =
2391
+ (this[layoutCyclePendingFlagsSymbol] || 0) | layoutReasons;
2392
+ this[layoutCyclePendingPrioritySymbol] = Math.max(
2393
+ this[layoutCyclePendingPrioritySymbol] || 0,
2394
+ layoutPriority,
2395
+ );
2396
+
2397
+ if (this[layoutCycleRunningSymbol] === true) {
2398
+ return;
2399
+ }
2400
+
2401
+ const pendingPriority = this[layoutCyclePendingPrioritySymbol];
2402
+ const currentMode = this[layoutCycleModeSymbol];
2403
+ if (
2404
+ currentMode === "microtask" ||
2405
+ (currentMode === "frame" &&
2406
+ pendingPriority !== SELECT_LAYOUT_PRIORITY_CRITICAL)
2407
+ ) {
2408
+ return;
2409
+ }
2410
+
2411
+ if (pendingPriority === SELECT_LAYOUT_PRIORITY_CRITICAL) {
2412
+ cancelScheduledSelectLayoutCycle.call(this);
2413
+ const token = this[layoutCycleTokenSymbol];
2414
+ this[layoutCycleModeSymbol] = "microtask";
2415
+ queueMicrotask(() => {
2416
+ if (
2417
+ this[layoutCycleModeSymbol] !== "microtask" ||
2418
+ this[layoutCycleTokenSymbol] !== token
2419
+ ) {
2420
+ return;
2421
+ }
2422
+
2423
+ delete this[layoutCycleModeSymbol];
2424
+ flushScheduledSelectLayoutCycle.call(this);
2425
+ });
2426
+ return;
2427
+ }
2428
+
2429
+ const globalObject = getGlobal();
2430
+ const schedule =
2431
+ globalObject?.requestAnimationFrame instanceof Function
2432
+ ? globalObject.requestAnimationFrame.bind(globalObject)
2433
+ : (callback) => {
2434
+ return globalObject.setTimeout(callback, 16);
2435
+ };
2436
+
2437
+ this[layoutCycleModeSymbol] = "frame";
2438
+ const token = (this[layoutCycleTokenSymbol] || 0) + 1;
2439
+ this[layoutCycleTokenSymbol] = token;
2440
+ this[layoutCycleFrameSymbol] = schedule(() => {
2441
+ delete this[layoutCycleFrameSymbol];
2442
+ if (
2443
+ this[layoutCycleModeSymbol] !== "frame" ||
2444
+ this[layoutCycleTokenSymbol] !== token
2445
+ ) {
2446
+ return;
2447
+ }
2448
+
2449
+ delete this[layoutCycleModeSymbol];
2450
+ flushScheduledSelectLayoutCycle.call(this);
2451
+ });
2452
+ }
2453
+
2239
2454
  /**
2240
2455
  * @private
2241
2456
  * @returns {string}
@@ -2327,6 +2542,27 @@ function buildSelectionLabel(value) {
2327
2542
  return map.get(key);
2328
2543
  }
2329
2544
 
2545
+ const options = this.getOption("options");
2546
+ if (isArray(options)) {
2547
+ for (const option of options) {
2548
+ if (!isObject(option) || option.value === undefined) {
2549
+ continue;
2550
+ }
2551
+
2552
+ const optionKey = strict
2553
+ ? option.value
2554
+ : getSelectionStateKey.call(this, option.value);
2555
+ if (optionKey !== key) {
2556
+ continue;
2557
+ }
2558
+
2559
+ if (clearUnresolvedSelectionValue.call(this, value)) {
2560
+ this[lookupCacheSymbol].delete(getSelectionCacheKey.call(this, value));
2561
+ }
2562
+ return option.label;
2563
+ }
2564
+ }
2565
+
2330
2566
  const cacheKey = getSelectionCacheKey.call(this, value);
2331
2567
  if (this[lookupCacheSymbol].has(cacheKey)) {
2332
2568
  return this[lookupCacheSymbol].get(cacheKey);
@@ -2773,19 +3009,21 @@ function setTotalText() {
2773
3009
  }
2774
3010
 
2775
3011
  if (this[isLoadingSymbol] === true) {
2776
- this.setOption("messages.total", "");
3012
+ setRemoteInfoText.call(this, "", {
3013
+ reserveSpace: true,
3014
+ });
2777
3015
  return;
2778
3016
  }
2779
3017
 
2780
3018
  const count = this.getOption("options").length;
2781
3019
  if (count === 0) {
2782
- this.setOption("messages.total", "");
3020
+ setRemoteInfoText.call(this, "");
2783
3021
  return;
2784
3022
  }
2785
3023
 
2786
3024
  const total = Number.parseInt(this.getOption("total"));
2787
3025
  if (Number.isNaN(total)) {
2788
- this.setOption("messages.total", "");
3026
+ setRemoteInfoText.call(this, "");
2789
3027
  return;
2790
3028
  }
2791
3029
 
@@ -2793,7 +3031,7 @@ function setTotalText() {
2793
3031
 
2794
3032
  const diff = total - count;
2795
3033
  if (diff < 0) {
2796
- this.setOption("messages.total", "");
3034
+ setRemoteInfoText.call(this, "");
2797
3035
  return;
2798
3036
  }
2799
3037
  const text = translations.getPluralRuleText("total", diff, "");
@@ -2801,7 +3039,7 @@ function setTotalText() {
2801
3039
  count: String(diff),
2802
3040
  }).format(text);
2803
3041
 
2804
- this.setOption("messages.total", selectedText);
3042
+ setRemoteInfoText.call(this, selectedText);
2805
3043
  }
2806
3044
 
2807
3045
  /**
@@ -2959,6 +3197,7 @@ function calcAndSetOptionsDimension() {
2959
3197
  this[popperElementSymbol].dataset.monsterPreferredWidth = `${Math.ceil(
2960
3198
  widthConstraints.preferredWidth,
2961
3199
  )}`;
3200
+ this[popperElementSymbol].dataset.monsterWidthBehavior = "preferred";
2962
3201
  if (
2963
3202
  Number.isFinite(widthConstraints.maxWidth) &&
2964
3203
  widthConstraints.maxWidth > 0
@@ -2973,8 +3212,12 @@ function calcAndSetOptionsDimension() {
2973
3212
  delete this[popperElementSymbol].dataset.monsterPreferredMaxWidth;
2974
3213
  this[popperElementSymbol].style.removeProperty("maxWidth");
2975
3214
  }
2976
- this[popperElementSymbol].style.width = "";
2977
- this[popperElementSymbol].style.removeProperty("minWidth");
3215
+ this[popperElementSymbol].style.width = `${Math.ceil(
3216
+ Math.min(widthConstraints.preferredWidth, widthConstraints.maxWidth),
3217
+ )}px`;
3218
+ this[popperElementSymbol].style.minWidth = `${Math.ceil(
3219
+ Math.min(widthConstraints.preferredWidth, widthConstraints.maxWidth),
3220
+ )}px`;
2978
3221
  this[popperElementSymbol].style.maxHeight = `${Math.ceil(
2979
3222
  Math.min(geometry.availableHeight, SELECT_MAX_POPPER_HEIGHT),
2980
3223
  )}px`;
@@ -3227,6 +3470,7 @@ function resetSelectPopperDimensionStyles() {
3227
3470
 
3228
3471
  delete this[popperElementSymbol].dataset.monsterPreferredWidth;
3229
3472
  delete this[popperElementSymbol].dataset.monsterPreferredMaxWidth;
3473
+ delete this[popperElementSymbol].dataset.monsterWidthBehavior;
3230
3474
  this[popperElementSymbol].style.removeProperty("width");
3231
3475
  this[popperElementSymbol].style.removeProperty("minWidth");
3232
3476
  this[popperElementSymbol].style.removeProperty("maxWidth");
@@ -3355,12 +3599,16 @@ function filterOptions() {
3355
3599
  }
3356
3600
  }
3357
3601
  })
3358
- .run()
3359
- .then(() => {
3360
- new Processing(100, () => {
3361
- calcAndSetOptionsDimension.call(this);
3362
- focusFilter.call(this);
3363
- })
3602
+ .run()
3603
+ .then(() => {
3604
+ new Processing(100, () => {
3605
+ scheduleSelectLayoutCycle.call(
3606
+ this,
3607
+ SELECT_LAYOUT_PRIORITY_CRITICAL,
3608
+ SELECT_LAYOUT_REASON_PAGINATION | SELECT_LAYOUT_REASON_POSITION,
3609
+ );
3610
+ focusFilter.call(this);
3611
+ })
3364
3612
  .run()
3365
3613
  .catch((e) => {
3366
3614
  addErrorAttribute(this, e);
@@ -4531,12 +4779,13 @@ function show() {
4531
4779
  } else {
4532
4780
  initTotal.call(self);
4533
4781
  }
4534
- calcAndSetOptionsDimension.call(this);
4535
- focusFilter.call(this);
4536
- this[popperElementSymbol].style.removeProperty("visibility");
4537
- refreshSelectPaginationLayout.call(this);
4538
- updatePopper.call(this);
4539
- })
4782
+ focusFilter.call(this);
4783
+ return performSelectLayoutCycle
4784
+ .call(this, SELECT_LAYOUT_REASON_ALL)
4785
+ .then(() => {
4786
+ this[popperElementSymbol].style.removeProperty("visibility");
4787
+ });
4788
+ })
4540
4789
  .run()
4541
4790
  .catch((e) => {
4542
4791
  addErrorAttribute(this, e);
@@ -4621,7 +4870,9 @@ function initTotal() {
4621
4870
  }
4622
4871
 
4623
4872
  const fetchOptions = this.getOption("fetch", {});
4624
- this.setOption("messages.total", "");
4873
+ setRemoteInfoText.call(this, "", {
4874
+ reserveSpace: true,
4875
+ });
4625
4876
 
4626
4877
  const remoteInfoRequest = getGlobal()
4627
4878
  .fetch(url, fetchOptions)
@@ -4678,7 +4929,7 @@ function resetPaginationState(clearTotalMessage = true) {
4678
4929
  paginationElement.setOption("currentPage", null);
4679
4930
  paginationElement.setOption("objectsPerPage", null);
4680
4931
  if (clearTotalMessage === true) {
4681
- this.setOption("messages.total", "");
4932
+ setRemoteInfoText.call(this, "");
4682
4933
  }
4683
4934
  }
4684
4935
 
@@ -4689,13 +4940,21 @@ function clearOptionsOnError() {
4689
4940
 
4690
4941
  function refreshSelectPaginationLayout() {
4691
4942
  const paginationElement = this[paginationElementSymbol];
4692
- if (!paginationElement || typeof paginationElement.getOption !== "function") {
4943
+ if (!paginationElement) {
4944
+ return;
4945
+ }
4946
+
4947
+ if (typeof paginationElement.refreshLayout === "function") {
4948
+ paginationElement.refreshLayout();
4949
+ return;
4950
+ }
4951
+
4952
+ if (typeof paginationElement.getOption !== "function") {
4693
4953
  return;
4694
4954
  }
4695
4955
 
4696
4956
  const currentPage = paginationElement.getOption("currentPage");
4697
4957
  const totalPages = paginationElement.getOption("pages");
4698
-
4699
4958
  if (!isInteger(currentPage) || !isInteger(totalPages)) {
4700
4959
  return;
4701
4960
  }
@@ -4953,15 +5212,17 @@ function initEventHandler() {
4953
5212
  }
4954
5213
  }
4955
5214
 
4956
- this[debounceOptionsMutationObserverSymbol] = new DeadMansSwitch(
4957
- 100,
4958
- () => {
4959
- checkOptionState.call(self);
4960
- calcAndSetOptionsDimension.call(self);
4961
- updatePopper.call(self);
4962
- delete this[debounceOptionsMutationObserverSymbol];
4963
- },
4964
- );
5215
+ this[debounceOptionsMutationObserverSymbol] = new DeadMansSwitch(
5216
+ 100,
5217
+ () => {
5218
+ scheduleSelectLayoutCycle.call(
5219
+ self,
5220
+ SELECT_LAYOUT_PRIORITY_INTERACTIVE,
5221
+ SELECT_LAYOUT_REASON_ALL,
5222
+ );
5223
+ delete this[debounceOptionsMutationObserverSymbol];
5224
+ },
5225
+ );
4965
5226
  };
4966
5227
 
4967
5228
  const observer = new MutationObserver(callback);
@@ -5134,7 +5395,7 @@ function initControlReferences() {
5134
5395
  `[${ATTRIBUTE_ROLE}=popper]`,
5135
5396
  );
5136
5397
  this[popperElementSymbol].monsterBeforeFloatingUpdate = () => {
5137
- calcAndSetOptionsDimension.call(this);
5398
+ applySelectLayoutState.call(this, SELECT_LAYOUT_REASON_PAGINATION);
5138
5399
  };
5139
5400
  this[inlineFilterElementSymbol] = this.shadowRoot.querySelector(
5140
5401
  `[${ATTRIBUTE_ROLE}=filter][name="inline-filter"]`,
@@ -5164,31 +5425,11 @@ function initControlReferences() {
5164
5425
  * @private
5165
5426
  */
5166
5427
  function updatePopper() {
5167
- if (!isPositionedPopperOpen(this[popperElementSymbol])) {
5168
- return;
5169
- }
5170
-
5171
- if (this.getOption("disabled", false) === true) {
5172
- return;
5173
- }
5174
-
5175
- new Processing(() => {
5176
- calcAndSetOptionsDimension.call(this);
5177
- positionPopper.call(
5178
- this,
5179
- this[controlElementSymbol],
5180
- this[popperElementSymbol],
5181
- getSelectPopperPositionOptions.call(this),
5182
- );
5183
- requestAnimationFrame(() => {
5184
- refreshSelectPaginationLayout.call(this);
5185
- });
5186
- })
5187
- .run()
5188
- .catch((e) => {
5189
- addErrorAttribute(this, e);
5190
- });
5191
-
5428
+ scheduleSelectLayoutCycle.call(
5429
+ this,
5430
+ SELECT_LAYOUT_PRIORITY_INTERACTIVE,
5431
+ SELECT_LAYOUT_REASON_ALL,
5432
+ );
5192
5433
  return this;
5193
5434
  }
5194
5435
 
@@ -41,6 +41,7 @@ const settlingFrameMap = new WeakMap();
41
41
  const floatingResizeObserverMap = new WeakMap();
42
42
  const floatingSyncCycleMap = new WeakMap();
43
43
  const floatingAppearanceFrameMap = new WeakMap();
44
+ const floatingAppearanceTimeoutMap = new WeakMap();
44
45
 
45
46
  /**
46
47
  * @private
@@ -639,6 +640,18 @@ function syncPreferredFloatingWidth(floatingElement, maxWidth) {
639
640
  if (!Number.isFinite(preferredWidth) || preferredWidth <= 0) {
640
641
  return;
641
642
  }
643
+ const widthBehavior = floatingElement.dataset.monsterWidthBehavior;
644
+ if (widthBehavior === "preferred") {
645
+ const nextFloatingWidth = clampAvailableDimension(
646
+ preferredWidth,
647
+ Number.isFinite(maxWidth) ? maxWidth : Infinity,
648
+ );
649
+ if (Number.isFinite(nextFloatingWidth) && nextFloatingWidth > 0) {
650
+ floatingElement.style.width = `${nextFloatingWidth}px`;
651
+ floatingElement.style.minWidth = `${nextFloatingWidth}px`;
652
+ }
653
+ return;
654
+ }
642
655
 
643
656
  const contentElement = getFloatingContentElement(floatingElement);
644
657
  if (!(contentElement instanceof HTMLElement)) {
@@ -1036,6 +1049,15 @@ function scheduleFloatingAppearanceOpen(popperElement) {
1036
1049
  return;
1037
1050
  }
1038
1051
 
1052
+ const finishAppearanceOpen = () => {
1053
+ cancelFloatingAppearanceFrame(popperElement);
1054
+
1055
+ if (!isPositionedPopperOpen(popperElement)) {
1056
+ return;
1057
+ }
1058
+
1059
+ popperElement.dataset.monsterAppearance = "open";
1060
+ };
1039
1061
  const schedule =
1040
1062
  typeof requestAnimationFrame === "function"
1041
1063
  ? requestAnimationFrame
@@ -1043,35 +1065,40 @@ function scheduleFloatingAppearanceOpen(popperElement) {
1043
1065
  return setTimeout(callback, 16);
1044
1066
  };
1045
1067
  const frameId = schedule(() => {
1046
- floatingAppearanceFrameMap.delete(popperElement);
1047
-
1048
- if (!isPositionedPopperOpen(popperElement)) {
1049
- return;
1050
- }
1051
-
1052
- popperElement.dataset.monsterAppearance = "open";
1068
+ finishAppearanceOpen();
1053
1069
  });
1070
+ const timeoutId = setTimeout(() => {
1071
+ finishAppearanceOpen();
1072
+ }, 24);
1054
1073
 
1055
1074
  floatingAppearanceFrameMap.set(popperElement, frameId);
1075
+ floatingAppearanceTimeoutMap.set(popperElement, timeoutId);
1056
1076
  }
1057
1077
 
1058
1078
  function cancelFloatingAppearanceFrame(popperElement) {
1059
1079
  const frameId = floatingAppearanceFrameMap.get(popperElement);
1060
1080
  if (
1061
- frameId === undefined ||
1062
- frameId === null ||
1063
- Number.isNaN(frameId) === true
1081
+ frameId !== undefined &&
1082
+ frameId !== null &&
1083
+ Number.isNaN(frameId) === false
1064
1084
  ) {
1065
- return;
1085
+ if (typeof cancelAnimationFrame === "function") {
1086
+ cancelAnimationFrame(frameId);
1087
+ } else {
1088
+ clearTimeout(frameId);
1089
+ }
1066
1090
  }
1091
+ floatingAppearanceFrameMap.delete(popperElement);
1067
1092
 
1068
- if (typeof cancelAnimationFrame === "function") {
1069
- cancelAnimationFrame(frameId);
1070
- } else {
1071
- clearTimeout(frameId);
1093
+ const timeoutId = floatingAppearanceTimeoutMap.get(popperElement);
1094
+ if (
1095
+ timeoutId !== undefined &&
1096
+ timeoutId !== null &&
1097
+ Number.isNaN(timeoutId) === false
1098
+ ) {
1099
+ clearTimeout(timeoutId);
1072
1100
  }
1073
-
1074
- floatingAppearanceFrameMap.delete(popperElement);
1101
+ floatingAppearanceTimeoutMap.delete(popperElement);
1075
1102
  }
1076
1103
 
1077
1104
  function applyFloatingArrowStyles(arrowElement, placement, arrowData) {
@@ -7,6 +7,7 @@ let expect = chai.expect;
7
7
  chai.use(chaiDom);
8
8
 
9
9
  let Pagination;
10
+ let EmbeddedPagination;
10
11
 
11
12
  describe('Pagination', function () {
12
13
  this.timeout(10000);
@@ -15,6 +16,9 @@ describe('Pagination', function () {
15
16
  initJSDOM().then(() => {
16
17
  import("../../../../source/components/datatable/pagination.mjs").then((m) => {
17
18
  Pagination = m['Pagination'];
19
+ return import("../../../../source/components/datatable/embedded-pagination.mjs");
20
+ }).then((m) => {
21
+ EmbeddedPagination = m['EmbeddedPagination'];
18
22
  done();
19
23
  }).catch(e => done(e));
20
24
  });
@@ -90,10 +94,65 @@ describe('Pagination', function () {
90
94
  }, 0);
91
95
  });
92
96
 
97
+ it('keeps adaptive pagination visible when the same state is applied again', function (done) {
98
+ const mocks = document.getElementById('mocks');
99
+ const wrapper = document.createElement('div');
100
+ const control = document.createElement('monster-embedded-pagination');
101
+
102
+ wrapper.getBoundingClientRect = () => ({
103
+ width: 320,
104
+ height: 40,
105
+ top: 0,
106
+ left: 0,
107
+ right: 320,
108
+ bottom: 40,
109
+ x: 0,
110
+ y: 0
111
+ });
112
+
113
+ mocks.appendChild(wrapper);
114
+ wrapper.appendChild(control);
115
+
116
+ setTimeout(() => {
117
+ try {
118
+ control.setPaginationState({currentPage: 2, totalPages: 5});
119
+
120
+ setTimeout(() => {
121
+ try {
122
+ const list = control.shadowRoot.querySelector('.pagination-list');
123
+ expect(list).to.exist;
124
+
125
+ list.setAttribute('data-monster-adaptive-ready', 'true');
126
+ control.setPaginationState({currentPage: 2, totalPages: 5});
127
+
128
+ expect(list.getAttribute('data-monster-adaptive-ready')).to.equal('true');
129
+
130
+ setTimeout(() => {
131
+ try {
132
+ expect(list.getAttribute('data-monster-adaptive-ready')).to.equal('true');
133
+ done();
134
+ } catch (e) {
135
+ done(e);
136
+ }
137
+ }, 50);
138
+ } catch (e) {
139
+ done(e);
140
+ }
141
+ }, 0);
142
+ } catch (e) {
143
+ done(e);
144
+ }
145
+ }, 0);
146
+ });
147
+
93
148
  describe('document.createElement', function () {
94
149
  it('should instance of Pagination', function () {
95
150
  expect(document.createElement('monster-pagination')).is.instanceof(Pagination);
96
151
  });
152
+
153
+ it('should instance of EmbeddedPagination', function () {
154
+ expect(document.createElement('monster-embedded-pagination')).is.instanceof(EmbeddedPagination);
155
+ });
97
156
  });
98
157
 
99
158
  });
@@ -237,4 +237,77 @@ describe("form floating-ui boundary resolution", function () {
237
237
  expect(options.style.height).to.equal("72px");
238
238
  expect(options.style.maxHeight).to.equal("72px");
239
239
  });
240
+
241
+ it("should keep a preferred-width popper stable instead of growing with content", function () {
242
+ const mocks = document.getElementById("mocks");
243
+ const popper = document.createElement("div");
244
+ const header = document.createElement("div");
245
+ const content = document.createElement("div");
246
+ const options = document.createElement("div");
247
+
248
+ popper.dataset.monsterPreferredWidth = "240";
249
+ popper.dataset.monsterWidthBehavior = "preferred";
250
+ popper.style.maxWidth = "600px";
251
+ content.setAttribute("part", "content");
252
+ options.style.overflowY = "auto";
253
+
254
+ Object.defineProperty(content, "scrollWidth", {
255
+ configurable: true,
256
+ value: 620,
257
+ });
258
+ Object.defineProperty(options, "scrollWidth", {
259
+ configurable: true,
260
+ value: 620,
261
+ });
262
+
263
+ header.getBoundingClientRect = () => {
264
+ return {
265
+ width: 240,
266
+ height: 24,
267
+ top: 0,
268
+ left: 0,
269
+ right: 240,
270
+ bottom: 24,
271
+ x: 0,
272
+ y: 0,
273
+ };
274
+ };
275
+ content.getBoundingClientRect = () => {
276
+ return {
277
+ width: 220,
278
+ height: 120,
279
+ top: 0,
280
+ left: 0,
281
+ right: 220,
282
+ bottom: 120,
283
+ x: 0,
284
+ y: 0,
285
+ };
286
+ };
287
+ popper.getBoundingClientRect = () => {
288
+ return {
289
+ width: 240,
290
+ height: 160,
291
+ top: 0,
292
+ left: 0,
293
+ right: 240,
294
+ bottom: 160,
295
+ x: 0,
296
+ y: 0,
297
+ };
298
+ };
299
+
300
+ content.appendChild(options);
301
+ popper.appendChild(header);
302
+ popper.appendChild(content);
303
+ mocks.appendChild(popper);
304
+
305
+ applyAdaptiveFloatingElementSize(popper, {
306
+ availableWidth: 600,
307
+ availableHeight: 200,
308
+ });
309
+
310
+ expect(popper.style.width).to.equal("240px");
311
+ expect(popper.style.minWidth).to.equal("240px");
312
+ });
240
313
  });
@@ -235,4 +235,44 @@ describe("PopperButton", function () {
235
235
  }
236
236
  }, 0);
237
237
  });
238
+
239
+ it("should finish the opening appearance state when requestAnimationFrame stalls", function (done) {
240
+ let mocks = document.getElementById("mocks");
241
+ const button = document.createElement("monster-popper-button");
242
+ const originalRequestAnimationFrame = globalThis.requestAnimationFrame;
243
+ const originalCancelAnimationFrame = globalThis.cancelAnimationFrame;
244
+
245
+ globalThis.requestAnimationFrame = () => 1;
246
+ globalThis.cancelAnimationFrame = () => {};
247
+ mocks.appendChild(button);
248
+
249
+ setTimeout(() => {
250
+ try {
251
+ button.showDialog();
252
+
253
+ const popper = button.shadowRoot.querySelector(
254
+ '[data-monster-role="popper"]',
255
+ );
256
+ expect(popper).to.exist;
257
+ expect(popper.dataset.monsterAppearance).to.equal("opening");
258
+
259
+ setTimeout(() => {
260
+ try {
261
+ expect(popper.dataset.monsterAppearance).to.equal("open");
262
+ button.hideDialog();
263
+ done();
264
+ } catch (e) {
265
+ done(e);
266
+ } finally {
267
+ globalThis.requestAnimationFrame = originalRequestAnimationFrame;
268
+ globalThis.cancelAnimationFrame = originalCancelAnimationFrame;
269
+ }
270
+ }, 30);
271
+ } catch (e) {
272
+ globalThis.requestAnimationFrame = originalRequestAnimationFrame;
273
+ globalThis.cancelAnimationFrame = originalCancelAnimationFrame;
274
+ done(e);
275
+ }
276
+ }, 0);
277
+ });
238
278
  });
@@ -47,6 +47,18 @@ function createJsonResponse(data, status = 200) {
47
47
  });
48
48
  }
49
49
 
50
+ function createDeferred() {
51
+ let resolve;
52
+ let reject;
53
+
54
+ const promise = new Promise((res, rej) => {
55
+ resolve = res;
56
+ reject = rej;
57
+ });
58
+
59
+ return {promise, resolve, reject};
60
+ }
61
+
50
62
  function waitForCondition(check, {timeout = 4000, interval = 25} = {}) {
51
63
  return new Promise((resolve, reject) => {
52
64
  const start = Date.now();
@@ -284,7 +296,8 @@ describe('Select', function () {
284
296
  shadowRoot.querySelector('[data-monster-role=container]').click();
285
297
  setTimeout(() => {
286
298
  try {
287
- expect(popper.style.minWidth).to.equal('');
299
+ expect(popper.style.minWidth).to.equal('240px');
300
+ expect(popper.dataset.monsterWidthBehavior).to.equal('preferred');
288
301
  expect(popper.dataset.monsterPreferredWidth).to.equal('240');
289
302
  done();
290
303
  } catch (e) {
@@ -1140,6 +1153,195 @@ describe('Select', function () {
1140
1153
  expect(select.getOption('messages.total')).to.contain('No additional entries are available');
1141
1154
  });
1142
1155
 
1156
+ it('should keep the remote-info footer height stable while a remote page request is pending', async function () {
1157
+ this.timeout(4000);
1158
+
1159
+ let mocks = document.getElementById('mocks');
1160
+ const firstRequest = createDeferred();
1161
+ const secondRequest = createDeferred();
1162
+ let requestCount = 0;
1163
+
1164
+ global['fetch'] = function () {
1165
+ requestCount += 1;
1166
+
1167
+ if (requestCount === 1) {
1168
+ return firstRequest.promise;
1169
+ }
1170
+
1171
+ if (requestCount === 2) {
1172
+ return secondRequest.promise;
1173
+ }
1174
+
1175
+ return Promise.reject(new Error('unexpected fetch request'));
1176
+ };
1177
+
1178
+ const select = document.createElement('monster-select');
1179
+ select.setOption('url', 'https://example.com/items?filter={filter}&page={page}');
1180
+ select.setOption('filter.mode', 'remote');
1181
+ select.setOption('filter.position', 'popper');
1182
+ select.setOption('mapping.selector', 'items.*');
1183
+ select.setOption('mapping.labelTemplate', '${name}');
1184
+ select.setOption('mapping.valueTemplate', '${id}');
1185
+ select.setOption('mapping.total', 'pagination.total');
1186
+ select.setOption('mapping.currentPage', 'pagination.page');
1187
+ select.setOption('mapping.objectsPerPage', 'pagination.perPage');
1188
+ mocks.appendChild(select);
1189
+
1190
+ await waitForCondition(() => {
1191
+ return select.shadowRoot.querySelector('[data-monster-role=container]') instanceof HTMLElement;
1192
+ });
1193
+
1194
+ const firstFetch = select.fetch('https://example.com/items?filter=*&page=1');
1195
+ firstRequest.resolve(await createJsonResponse({
1196
+ items: [
1197
+ {id: 'alpha', name: 'Alpha'}
1198
+ ],
1199
+ pagination: {
1200
+ total: 12,
1201
+ page: 1,
1202
+ perPage: 1
1203
+ }
1204
+ }));
1205
+ await firstFetch;
1206
+
1207
+ await waitForCondition(() => {
1208
+ return select.getOption('messages.total').includes('additional entries are available');
1209
+ });
1210
+
1211
+ const container = select.shadowRoot.querySelector('[data-monster-role=container]');
1212
+ const remoteInfoElement = select.shadowRoot.querySelector('[data-monster-role=remote-info]');
1213
+
1214
+ container.click();
1215
+
1216
+ await waitForCondition(() => {
1217
+ return select.shadowRoot
1218
+ .querySelector('[data-monster-role=control]')
1219
+ .classList
1220
+ .contains('open');
1221
+ });
1222
+
1223
+ expect(remoteInfoElement.style.visibility).to.equal('');
1224
+
1225
+ const secondFetch = select.fetch('https://example.com/items?filter=*&page=2');
1226
+
1227
+ await waitForCondition(() => {
1228
+ return remoteInfoElement.style.visibility === 'hidden';
1229
+ });
1230
+
1231
+ expect(select.getOption('messages.total')).to.contain('additional entries are available');
1232
+
1233
+ secondRequest.resolve(await createJsonResponse({
1234
+ items: [
1235
+ {id: 'beta', name: 'Beta'}
1236
+ ],
1237
+ pagination: {
1238
+ total: 12,
1239
+ page: 2,
1240
+ perPage: 1
1241
+ }
1242
+ }));
1243
+
1244
+ await secondFetch;
1245
+
1246
+ await waitForCondition(() => {
1247
+ return remoteInfoElement.style.visibility === '';
1248
+ });
1249
+
1250
+ expect(select.getOption('messages.total')).to.contain('additional entries are available');
1251
+ });
1252
+
1253
+ it('should clear preserved remote-info text after a later empty remote result settles', async function () {
1254
+ this.timeout(4000);
1255
+
1256
+ let mocks = document.getElementById('mocks');
1257
+ const firstRequest = createDeferred();
1258
+ const secondRequest = createDeferred();
1259
+ let requestCount = 0;
1260
+
1261
+ global['fetch'] = function () {
1262
+ requestCount += 1;
1263
+
1264
+ if (requestCount === 1) {
1265
+ return firstRequest.promise;
1266
+ }
1267
+
1268
+ if (requestCount === 2) {
1269
+ return secondRequest.promise;
1270
+ }
1271
+
1272
+ return Promise.reject(new Error('unexpected fetch request'));
1273
+ };
1274
+
1275
+ const select = document.createElement('monster-select');
1276
+ select.setOption('url', 'https://example.com/items?filter={filter}&page={page}');
1277
+ select.setOption('filter.mode', 'remote');
1278
+ select.setOption('filter.position', 'popper');
1279
+ select.setOption('mapping.selector', 'items.*');
1280
+ select.setOption('mapping.labelTemplate', '${name}');
1281
+ select.setOption('mapping.valueTemplate', '${id}');
1282
+ select.setOption('mapping.total', 'pagination.total');
1283
+ select.setOption('mapping.currentPage', 'pagination.page');
1284
+ select.setOption('mapping.objectsPerPage', 'pagination.perPage');
1285
+ mocks.appendChild(select);
1286
+
1287
+ await waitForCondition(() => {
1288
+ return select.shadowRoot.querySelector('[data-monster-role=container]') instanceof HTMLElement;
1289
+ });
1290
+
1291
+ const firstFetch = select.fetch('https://example.com/items?filter=*&page=1');
1292
+ firstRequest.resolve(await createJsonResponse({
1293
+ items: [
1294
+ {id: 'alpha', name: 'Alpha'}
1295
+ ],
1296
+ pagination: {
1297
+ total: 12,
1298
+ page: 1,
1299
+ perPage: 1
1300
+ }
1301
+ }));
1302
+ await firstFetch;
1303
+
1304
+ await waitForCondition(() => {
1305
+ return select.getOption('messages.total').includes('additional entries are available');
1306
+ });
1307
+
1308
+ const container = select.shadowRoot.querySelector('[data-monster-role=container]');
1309
+ const remoteInfoElement = select.shadowRoot.querySelector('[data-monster-role=remote-info]');
1310
+
1311
+ container.click();
1312
+
1313
+ await waitForCondition(() => {
1314
+ return select.shadowRoot
1315
+ .querySelector('[data-monster-role=control]')
1316
+ .classList
1317
+ .contains('open');
1318
+ });
1319
+
1320
+ const secondFetch = select.fetch('https://example.com/items?filter=leer&page=1');
1321
+
1322
+ await waitForCondition(() => {
1323
+ return remoteInfoElement.style.visibility === 'hidden';
1324
+ });
1325
+
1326
+ secondRequest.resolve(await createJsonResponse({
1327
+ items: [],
1328
+ pagination: {
1329
+ total: 0,
1330
+ page: 1,
1331
+ perPage: 1
1332
+ }
1333
+ }));
1334
+
1335
+ await secondFetch;
1336
+
1337
+ await waitForCondition(() => {
1338
+ return select.getOption('messages.total') === '';
1339
+ });
1340
+
1341
+ expect(remoteInfoElement.style.visibility).to.equal('');
1342
+ expect(select.getOption('messages.total')).to.equal('');
1343
+ });
1344
+
1143
1345
  it('should avoid duplicate remote-info badges for empty remote filter results', async function () {
1144
1346
  this.timeout(4000);
1145
1347