@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.
- package/CHANGELOG.md +2 -0
- package/package.json +1 -1
- package/source/components/form/select.mjs +135 -29
- package/source/components/form/util/floating-ui.mjs +19 -11
- package/source/components/form/util/popper.mjs +4 -0
- package/source/components/navigation/site-navigation.mjs +79 -58
- package/source/types/version.mjs +1 -1
- package/test/cases/components/form/message-state-button.mjs +36 -60
- package/test/cases/components/form/select.mjs +195 -0
- package/test/cases/monster.mjs +1 -1
- package/test/web/import.js +4 -0
- package/test/web/test.html +2 -2
- package/test/web/tests.js +6841 -2575
|
@@ -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(
|
|
77
|
-
.
|
|
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(
|
|
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 =
|
|
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(
|
|
245
|
-
"
|
|
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 =
|
|
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(
|
|
328
|
-
"
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
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
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
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
|
|
package/test/cases/monster.mjs
CHANGED
package/test/web/import.js
CHANGED
|
@@ -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";
|
package/test/web/test.html
CHANGED
|
@@ -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.
|
|
13
|
-
<div id="lastupdate" style='font-size:0.7em'>last update
|
|
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>
|