@schukai/monster 4.136.7 → 4.136.10

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.
@@ -73,8 +73,9 @@ describe("MessageStateButton", function () {
73
73
 
74
74
  describe("document.createElement", function () {
75
75
  it("should instance of message-state-button", function () {
76
- expect(document.createElement("monster-message-state-button")).is
77
- .instanceof(MessageStateButton);
76
+ expect(
77
+ document.createElement("monster-message-state-button"),
78
+ ).is.instanceof(MessageStateButton);
78
79
  });
79
80
  });
80
81
  });
@@ -93,9 +94,7 @@ describe("MessageStateButton", function () {
93
94
 
94
95
  setTimeout(() => {
95
96
  try {
96
- const inner = button.shadowRoot.querySelector(
97
- "monster-state-button",
98
- );
97
+ const inner = button.shadowRoot.querySelector("monster-state-button");
99
98
  expect(inner).to.exist;
100
99
 
101
100
  button.setAttribute("disabled", "");
@@ -231,19 +230,22 @@ describe("MessageStateButton", function () {
231
230
 
232
231
  setTimeout(() => {
233
232
  try {
234
- button.setMessage("<div><strong>Saved</strong><p>plain html</p></div>");
233
+ button.setMessage(
234
+ "<div><strong>Saved</strong><p>plain html</p></div>",
235
+ );
235
236
  button.showMessage();
236
237
 
237
238
  setTimeout(() => {
238
239
  try {
239
- const content = button.shadowRoot.querySelector('[part="content"]');
240
+ const content =
241
+ button.shadowRoot.querySelector('[part="content"]');
240
242
  const message = button.shadowRoot.querySelector(
241
243
  '[data-monster-role="message"]',
242
244
  );
243
245
  expect(content).to.exist;
244
- expect(content.getAttribute("data-monster-overflow-mode")).to.equal(
245
- "both",
246
- );
246
+ expect(
247
+ content.getAttribute("data-monster-overflow-mode"),
248
+ ).to.equal("both");
247
249
  expect(
248
250
  content.getAttribute("data-monster-message-layout"),
249
251
  ).to.equal("prose");
@@ -306,10 +308,7 @@ describe("MessageStateButton", function () {
306
308
  try {
307
309
  const wrapper = document.createElement("div");
308
310
  const line = document.createElement("div");
309
- line.setAttribute(
310
- "style",
311
- "white-space: nowrap; overflow-x: auto;",
312
- );
311
+ line.setAttribute("style", "white-space: nowrap; overflow-x: auto;");
313
312
  line.textContent =
314
313
  "this is intentionally a single long line to trigger wide layout";
315
314
  wrapper.appendChild(line);
@@ -319,14 +318,15 @@ describe("MessageStateButton", function () {
319
318
 
320
319
  setTimeout(() => {
321
320
  try {
322
- const content = button.shadowRoot.querySelector('[part="content"]');
321
+ const content =
322
+ button.shadowRoot.querySelector('[part="content"]');
323
323
  const message = button.shadowRoot.querySelector(
324
324
  '[data-monster-role="message"]',
325
325
  );
326
326
  expect(content).to.exist;
327
- expect(content.getAttribute("data-monster-overflow-mode")).to.equal(
328
- "both",
329
- );
327
+ expect(
328
+ content.getAttribute("data-monster-overflow-mode"),
329
+ ).to.equal("both");
330
330
  expect(
331
331
  content.getAttribute("data-monster-message-layout"),
332
332
  ).to.equal("wide");
@@ -351,7 +351,8 @@ describe("MessageStateButton", function () {
351
351
 
352
352
  beforeEach(() => {
353
353
  originalInnerWidth = window.innerWidth;
354
- originalGetBoundingClientRect = HTMLElement.prototype.getBoundingClientRect;
354
+ originalGetBoundingClientRect =
355
+ HTMLElement.prototype.getBoundingClientRect;
355
356
  });
356
357
 
357
358
  afterEach(() => {
@@ -360,7 +361,8 @@ describe("MessageStateButton", function () {
360
361
  writable: true,
361
362
  value: originalInnerWidth,
362
363
  });
363
- HTMLElement.prototype.getBoundingClientRect = originalGetBoundingClientRect;
364
+ HTMLElement.prototype.getBoundingClientRect =
365
+ originalGetBoundingClientRect;
364
366
  let mocks = document.getElementById("mocks");
365
367
  mocks.innerHTML = "";
366
368
  });
@@ -388,14 +390,7 @@ describe("MessageStateButton", function () {
388
390
  };
389
391
  }
390
392
 
391
- return {
392
- width: 100,
393
- height: 40,
394
- top: 0,
395
- left: 0,
396
- right: 100,
397
- bottom: 40,
398
- };
393
+ return originalGetBoundingClientRect.call(this);
399
394
  };
400
395
 
401
396
  setTimeout(() => {
@@ -405,18 +400,12 @@ describe("MessageStateButton", function () {
405
400
  );
406
401
  button.showMessage();
407
402
 
408
- setTimeout(() => {
409
- try {
410
- const popper = button.shadowRoot.querySelector(
411
- '[data-monster-role="popper"]',
412
- );
413
- expect(popper.style.width).to.equal("512px");
414
- expect(popper.style.maxWidth).to.equal("512px");
415
- done();
416
- } catch (e) {
417
- done(e);
418
- }
419
- }, 0);
403
+ const popper = button.shadowRoot.querySelector(
404
+ '[data-monster-role="popper"]',
405
+ );
406
+ expect(popper.style.width).to.equal("512px");
407
+ expect(popper.style.maxWidth).to.equal("512px");
408
+ done();
420
409
  } catch (e) {
421
410
  done(e);
422
411
  }
@@ -446,14 +435,7 @@ describe("MessageStateButton", function () {
446
435
  };
447
436
  }
448
437
 
449
- return {
450
- width: 100,
451
- height: 40,
452
- top: 0,
453
- left: 0,
454
- right: 100,
455
- bottom: 40,
456
- };
438
+ return originalGetBoundingClientRect.call(this);
457
439
  };
458
440
 
459
441
  setTimeout(() => {
@@ -465,18 +447,12 @@ describe("MessageStateButton", function () {
465
447
  button.setMessage(wrapper);
466
448
  button.showMessage();
467
449
 
468
- setTimeout(() => {
469
- try {
470
- const popper = button.shadowRoot.querySelector(
471
- '[data-monster-role="popper"]',
472
- );
473
- expect(popper.style.width).to.equal("768px");
474
- expect(popper.style.maxWidth).to.equal("768px");
475
- done();
476
- } catch (e) {
477
- done(e);
478
- }
479
- }, 0);
450
+ const popper = button.shadowRoot.querySelector(
451
+ '[data-monster-role="popper"]',
452
+ );
453
+ expect(popper.style.width).to.equal("768px");
454
+ expect(popper.style.maxWidth).to.equal("768px");
455
+ done();
480
456
  } catch (e) {
481
457
  done(e);
482
458
  }
@@ -790,6 +790,155 @@ describe('Select', function () {
790
790
  }, 150);
791
791
  });
792
792
 
793
+ it('should not eagerly fetch remote info when showRemoteInfo is disabled', function (done) {
794
+ this.timeout(3000);
795
+
796
+ let mocks = document.getElementById('mocks');
797
+ const requests = [];
798
+ const previousFetch = global['fetch'];
799
+
800
+ global['fetch'] = function (url) {
801
+ requests.push(String(url));
802
+
803
+ return createJsonResponse({
804
+ items: [
805
+ {id: 'alpha', name: 'Alpha'}
806
+ ],
807
+ pagination: {
808
+ total: 3,
809
+ page: 2,
810
+ perPage: 1
811
+ }
812
+ });
813
+ };
814
+
815
+ const select = document.createElement('monster-select');
816
+ select.setOption('url', 'https://example.com/items?filter={filter}&page={page}');
817
+ select.setOption('filter.mode', 'remote');
818
+ select.setOption('mapping.selector', 'items.*');
819
+ select.setOption('mapping.labelTemplate', '${name}');
820
+ select.setOption('mapping.valueTemplate', '${id}');
821
+ select.setOption('mapping.total', 'pagination.total');
822
+ select.setOption('mapping.currentPage', 'pagination.page');
823
+ select.setOption('mapping.objectsPerPage', 'pagination.perPage');
824
+ select.setOption('remoteInfo.url', 'https://example.com/remote-info');
825
+ select.setOption('features.showRemoteInfo', false);
826
+ mocks.appendChild(select);
827
+
828
+ setTimeout(() => {
829
+ try {
830
+ expect(requests).to.deep.equal([]);
831
+ } catch (e) {
832
+ global['fetch'] = previousFetch;
833
+ return done(e);
834
+ }
835
+
836
+ select.fetch('https://example.com/items?filter=*&page=2')
837
+ .then(() => {
838
+ try {
839
+ const pagination = select.shadowRoot.querySelector('[data-monster-role=pagination]');
840
+
841
+ expect(requests).to.deep.equal([
842
+ 'https://example.com/items?filter=*&page=2'
843
+ ]);
844
+ expect(select.getOption('total')).to.equal(3);
845
+ expect(pagination.getOption('currentPage')).to.equal(2);
846
+ expect(pagination.getOption('pages')).to.equal(3);
847
+ expect(pagination.getOption('objectsPerPage')).to.equal(1);
848
+ } catch (e) {
849
+ return done(e);
850
+ } finally {
851
+ global['fetch'] = previousFetch;
852
+ }
853
+
854
+ done();
855
+ })
856
+ .catch((e) => {
857
+ global['fetch'] = previousFetch;
858
+ done(e);
859
+ });
860
+ }, 150);
861
+ });
862
+
863
+ it('should defer remote info fetching until the dropdown is opened', async function () {
864
+ this.timeout(4000);
865
+
866
+ let mocks = document.getElementById('mocks');
867
+ const requests = [];
868
+ const remoteInfoUrl = 'https://example.com/remote-info';
869
+
870
+ global['fetch'] = function (url) {
871
+ requests.push(String(url));
872
+
873
+ return createJsonResponse({
874
+ pagination: {
875
+ total: 5
876
+ }
877
+ });
878
+ };
879
+
880
+ const select = document.createElement('monster-select');
881
+ select.setOption('filter.mode', 'remote');
882
+ select.setOption('filter.position', 'popper');
883
+ select.setOption('mapping.total', 'pagination.total');
884
+ select.setOption('remoteInfo.url', remoteInfoUrl);
885
+ select.setOption('options', [
886
+ {label: 'Alpha', value: 'alpha'}
887
+ ]);
888
+ mocks.appendChild(select);
889
+
890
+ await waitForCondition(() => {
891
+ return select.shadowRoot.querySelector('[data-monster-role=container]') instanceof HTMLElement;
892
+ });
893
+
894
+ expect(requests).to.deep.equal([]);
895
+
896
+ const container = select.shadowRoot.querySelector('[data-monster-role=container]');
897
+
898
+ container.click();
899
+ await waitForCondition(() => requests.includes(remoteInfoUrl));
900
+ expect(requests.filter((url) => url === remoteInfoUrl)).to.have.length(1);
901
+ });
902
+
903
+ it('should keep empty equivalent matching type-safe for numeric zero', function (done) {
904
+ this.timeout(2000);
905
+
906
+ let mocks = document.getElementById('mocks');
907
+ const stringEquivalentSelect = document.createElement('monster-select');
908
+ stringEquivalentSelect.setOption('empty.equivalents', ['0']);
909
+ mocks.appendChild(stringEquivalentSelect);
910
+
911
+ const numericEquivalentSelect = document.createElement('monster-select');
912
+ numericEquivalentSelect.setOption('empty.equivalents', [0]);
913
+ mocks.appendChild(numericEquivalentSelect);
914
+
915
+ setTimeout(() => {
916
+ stringEquivalentSelect.value = 0;
917
+ numericEquivalentSelect.value = 0;
918
+
919
+ setTimeout(() => {
920
+ try {
921
+ expect(stringEquivalentSelect.value).to.equal('0');
922
+ expect(stringEquivalentSelect.getOption('selection')).to.deep.equal([
923
+ {
924
+ label: '0',
925
+ value: 0,
926
+ class: 'monster-badge-primary',
927
+ unresolved: false
928
+ }
929
+ ]);
930
+
931
+ expect(numericEquivalentSelect.value).to.equal('');
932
+ expect(numericEquivalentSelect.getOption('selection')).to.deep.equal([]);
933
+ } catch (e) {
934
+ return done(e);
935
+ }
936
+
937
+ done();
938
+ }, 50);
939
+ }, 50);
940
+ });
941
+
793
942
  it('should not refetch after selecting an already loaded remote option', function (done) {
794
943
  this.timeout(3000);
795
944
 
@@ -980,6 +1129,52 @@ describe('Select', function () {
980
1129
  }, 250);
981
1130
  });
982
1131
 
1132
+ it('should keep unresolved object lookup values visible by key and mark their badge', function (done) {
1133
+ this.timeout(3000);
1134
+
1135
+ let mocks = document.getElementById('mocks');
1136
+ const requests = [];
1137
+ global['fetch'] = function (url) {
1138
+ requests.push(url.toString());
1139
+ return createJsonResponse({
1140
+ items: [],
1141
+ pagination: {
1142
+ total: 0,
1143
+ page: 1,
1144
+ perPage: 1
1145
+ }
1146
+ });
1147
+ };
1148
+
1149
+ const select = document.createElement('monster-select');
1150
+ select.setOption('lookup.url', 'https://example.com/items?filter={filter}');
1151
+ select.setOption('mapping.selector', 'items.*');
1152
+ select.setOption('mapping.labelTemplate', '${name}');
1153
+ select.setOption('mapping.valueTemplate', '${id}');
1154
+ select.setOption('selection', [{value: {id: 'missing-key'}}]);
1155
+ mocks.appendChild(select);
1156
+
1157
+ setTimeout(() => {
1158
+ try {
1159
+ const badge = select.shadowRoot.querySelector('[data-monster-role=badge]');
1160
+ const badgeLabel = select.shadowRoot.querySelector('[data-monster-role=badge-label]');
1161
+
1162
+ expect(requests).to.include('https://example.com/items?filter=missing-key');
1163
+ expect(badge).to.be.instanceof(HTMLDivElement);
1164
+ expect(badgeLabel).to.be.instanceof(HTMLDivElement);
1165
+ expect(badgeLabel.textContent.trim()).to.equal('missing-key');
1166
+ expect(badge.className).to.contain('monster-badge-warning');
1167
+ expect(badge.className).to.not.contain('monster-badge-primary');
1168
+ expect(badge.getAttribute('data-monster-unresolved')).to.equal('true');
1169
+ expect(badgeLabel.textContent).to.not.contain(select.getOption('labels.cannot-be-loaded'));
1170
+ } catch (e) {
1171
+ return done(e);
1172
+ }
1173
+
1174
+ done();
1175
+ }, 250);
1176
+ });
1177
+
983
1178
  it('should clear the unresolved badge state once a local option can resolve the value', function (done) {
984
1179
  this.timeout(4000);
985
1180
 
@@ -7,7 +7,7 @@ describe('Monster', function () {
7
7
  let monsterVersion
8
8
 
9
9
  /** don´t touch, replaced by make with package.json version */
10
- monsterVersion = new Version("4.128.3")
10
+ monsterVersion = new Version("4.136.7")
11
11
 
12
12
  let m = getMonsterVersion();
13
13
 
@@ -9,14 +9,18 @@ import "../cases/components/form/buy-box.mjs";
9
9
  import "../cases/components/form/message-state-button.mjs";
10
10
  import "../cases/components/form/button-bar.mjs";
11
11
  import "../cases/components/form/reload.mjs";
12
+ import "../cases/components/form/context-help.mjs";
12
13
  import "../cases/components/form/state-button.mjs";
13
14
  import "../cases/components/form/select.mjs";
14
15
  import "../cases/components/form/login.mjs";
15
16
  import "../cases/components/form/confirm-button.mjs";
17
+ import "../cases/components/form/context-error.mjs";
16
18
  import "../cases/components/form/form.mjs";
17
19
  import "../cases/components/form/tree-select.mjs";
20
+ import "../cases/components/form/popper-button.mjs";
18
21
  import "../cases/components/form/wizard.mjs";
19
22
  import "../cases/components/form/button.mjs";
23
+ import "../cases/components/form/floating-ui.mjs";
20
24
  import "../cases/components/form/toggle-switch.mjs";
21
25
  import "../cases/components/form/template.mjs";
22
26
  import "../cases/components/notify/message.mjs";
@@ -9,8 +9,8 @@
9
9
  </head>
10
10
  <body>
11
11
  <div id="headline" style="display: flex;align-items: center;justify-content: center;flex-direction: column;">
12
- <h1 style='margin-bottom: 0.1em;'>Monster 4.128.3</h1>
13
- <div id="lastupdate" style='font-size:0.7em'>last update So 5. Apr 19:19:33 CEST 2026</div>
12
+ <h1 style='margin-bottom: 0.1em;'>Monster 4.136.7</h1>
13
+ <div id="lastupdate" style='font-size:0.7em'>last update Wed Apr 22 21:49:31 CEST 2026</div>
14
14
  </div>
15
15
  <div id="mocha-errors"
16
16
  style="color: red;font-weight: bold;display: flex;align-items: center;justify-content: center;flex-direction: column;margin:20px;"></div>