@schukai/monster 4.152.0 → 4.153.1

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 (52) hide show
  1. package/README.md +24 -1
  2. package/package.json +1 -1
  3. package/source/components/content/viewer/message.mjs +1 -0
  4. package/source/components/form/select.mjs +2 -0
  5. package/source/components/form/util/fetch.mjs +6 -7
  6. package/source/components/state/log.mjs +7 -0
  7. package/source/components/state/thread.mjs +11 -0
  8. package/source/components/time/month-calendar.mjs +80 -30
  9. package/source/data/buildmap.mjs +3 -2
  10. package/source/data/buildtree.mjs +0 -8
  11. package/source/data/pathfinder.mjs +48 -19
  12. package/source/data/transformer.mjs +2 -2
  13. package/source/dom/customcontrol.mjs +35 -9
  14. package/source/dom/customelement.mjs +21 -2
  15. package/source/dom/resource.mjs +19 -5
  16. package/source/dom/sanitize-html.mjs +34 -29
  17. package/source/dom/slotted.mjs +46 -31
  18. package/source/dom/template.mjs +4 -1
  19. package/source/dom/updater.mjs +51 -15
  20. package/source/types/node.mjs +1 -3
  21. package/source/types/proxyobserver.mjs +32 -10
  22. package/test/cases/components/datatable/drag-scroll.mjs +28 -1
  23. package/test/cases/components/form/button-bar.mjs +9 -6
  24. package/test/cases/components/form/confirm-button.mjs +4 -4
  25. package/test/cases/components/form/control-bar.mjs +73 -72
  26. package/test/cases/components/form/input-group.mjs +3 -1
  27. package/test/cases/components/form/password.mjs +2 -2
  28. package/test/cases/components/form/popper-button.mjs +2 -2
  29. package/test/cases/components/form/select-sort-callback.mjs +57 -0
  30. package/test/cases/components/form/select.mjs +2 -2
  31. package/test/cases/components/form/sheet.mjs +13 -1
  32. package/test/cases/components/layout/popper.mjs +3 -1
  33. package/test/cases/components/layout/tabs.mjs +4 -1
  34. package/test/cases/components/navigation/site-navigation.mjs +12 -26
  35. package/test/cases/components/navigation/table-of-content.mjs +3 -0
  36. package/test/cases/components/state/ticker-lifecycle.mjs +47 -0
  37. package/test/cases/components/time/month-calendar.mjs +100 -0
  38. package/test/cases/data/buildmap.mjs +14 -0
  39. package/test/cases/data/buildtree.mjs +49 -0
  40. package/test/cases/data/pathfinder.mjs +34 -0
  41. package/test/cases/data/transformer.mjs +33 -0
  42. package/test/cases/dom/customcontrol.mjs +87 -6
  43. package/test/cases/dom/customelement.mjs +54 -0
  44. package/test/cases/dom/ready.mjs +3 -3
  45. package/test/cases/dom/resource-availability.mjs +46 -0
  46. package/test/cases/dom/resourcemanager.mjs +12 -6
  47. package/test/cases/dom/sanitize-html.mjs +71 -0
  48. package/test/cases/dom/slotted-nodes.mjs +48 -1
  49. package/test/cases/dom/template.mjs +11 -0
  50. package/test/cases/dom/updater.mjs +119 -1
  51. package/test/cases/types/proxyobserver.mjs +75 -0
  52. package/test/cases/util/processing.mjs +3 -3
@@ -1,14 +1,61 @@
1
1
  import {expect} from 'chai';
2
2
  import {getSlottedNodes} from "../../../source/dom/slotted.mjs";
3
+ import {getSlottedElements} from "../../../source/dom/slotted.mjs";
4
+ import {initJSDOM} from "../../util/jsdom.mjs";
3
5
 
4
6
 
5
7
  describe('getSlottedNodes', () => {
6
8
 
9
+ before(async () => {
10
+ await initJSDOM();
11
+ });
12
+
7
13
 
8
14
  it('should return an empty Set if there is no ShadowRoot', () => {
9
15
  expect(getSlottedNodes).is.a('function');
10
16
  });
11
17
 
18
+ it('should ignore text nodes when applying an element query issue #567', () => {
19
+ const host = document.createElement('div');
20
+ host.append(' whitespace ');
21
+ const child = document.createElement('span');
22
+ child.className = 'match';
23
+ host.appendChild(child);
24
+ host.attachShadow({mode: 'open'}).innerHTML = '<slot></slot>';
25
+ document.body.appendChild(host);
26
+
27
+ expect([...getSlottedNodes.call(host, '.match')]).to.deep.equal([child]);
28
+ host.remove();
29
+ });
30
+
31
+ it('should match empty and spaced slot names without selector interpolation issue #567', () => {
32
+ const host = document.createElement('div');
33
+ const defaultChild = document.createElement('span');
34
+ const namedChild = document.createElement('span');
35
+ namedChild.slot = 'some slot';
36
+ host.append(defaultChild, namedChild);
37
+ host.attachShadow({mode: 'open'}).innerHTML =
38
+ '<slot name=""></slot><slot name="some slot"></slot>';
39
+ document.body.appendChild(host);
40
+
41
+ expect([...getSlottedElements.call(host, undefined, null)]).to.deep.equal([defaultChild]);
42
+ expect([...getSlottedElements.call(host, undefined, 'some slot')]).to.deep.equal([namedChild]);
43
+ host.remove();
44
+ });
45
+
46
+ it('should accept the internally managed closed shadow root issue #567', () => {
47
+ const host = document.createElement('div');
48
+ const child = document.createElement('span');
49
+ host.appendChild(child);
50
+ const root = host.attachShadow({mode: 'closed'});
51
+ root.innerHTML = '<slot></slot>';
52
+ document.body.appendChild(host);
53
+
54
+ expect(host.shadowRoot).to.equal(null);
55
+ expect([...getSlottedElements.call(host, undefined, undefined, root)])
56
+ .to.deep.equal([child]);
57
+ host.remove();
58
+ });
12
59
 
13
- });
14
60
 
61
+ });
@@ -81,6 +81,17 @@ describe('Template', function () {
81
81
  expect(t).is.instanceof(Template);
82
82
 
83
83
  })
84
+
85
+ it('should resolve from a text node context issue #567', function () {
86
+ const container = document.createElement('div');
87
+ const text = document.createTextNode('context');
88
+ container.appendChild(text);
89
+ document.body.appendChild(container);
90
+
91
+ const template = findDocumentTemplate('mytemplate', text);
92
+ expect(template).to.be.instanceof(Template);
93
+ container.remove();
94
+ });
84
95
  });
85
96
 
86
97
  describe('Thmened Template', function () {
@@ -780,7 +780,7 @@ describe("DOM", function () {
780
780
 
781
781
  [false, true].forEach((batchUpdates) => {
782
782
  it(`should bulk insert and reuse indexed entries with batchUpdates=${batchUpdates} issue #548`, async function () {
783
- this.timeout(5000);
783
+ this.timeout(20000);
784
784
  const mocks = document.getElementById("mocks");
785
785
  mocks.innerHTML = `
786
786
  <template id="bulk-row">
@@ -1665,6 +1665,78 @@ describe("DOM", function () {
1665
1665
  .catch((e) => done(new Error(e)));
1666
1666
  });
1667
1667
 
1668
+ it("should not move unchanged keyed nodes issue #564", async function () {
1669
+ const element = document.getElementById("test-patch");
1670
+ const a = document.createElement("span");
1671
+ const b = document.createElement("span");
1672
+ a.dataset.key = "a";
1673
+ b.dataset.key = "b";
1674
+
1675
+ const updater = new Updater(element, {
1676
+ text: "",
1677
+ contentNode: document.createElement("div"),
1678
+ fragmentNode: document.createDocumentFragment(),
1679
+ itemsArray: [],
1680
+ keyedItems: [a, b],
1681
+ keyedDataItems: [],
1682
+ items: [],
1683
+ });
1684
+ updater.setCallback("getPatchKey", (value) => value?.dataset?.key);
1685
+ await updater.run();
1686
+
1687
+ const target = document.getElementById("patch-keyed");
1688
+ let moves = 0;
1689
+ const appendChild = target.appendChild;
1690
+ const insertBefore = target.insertBefore;
1691
+ target.appendChild = function (...args) {
1692
+ moves++;
1693
+ return appendChild.apply(this, args);
1694
+ };
1695
+ target.insertBefore = function (...args) {
1696
+ moves++;
1697
+ return insertBefore.apply(this, args);
1698
+ };
1699
+
1700
+ updater.getSubject().keyedItems = [a, b];
1701
+ await new Promise((resolve) => setTimeout(resolve, 30));
1702
+
1703
+ expect(moves).to.equal(0);
1704
+ expect(Array.from(target.children)).to.deep.equal([a, b]);
1705
+ });
1706
+
1707
+ it("should dispose a keyed node replaced under the same key issue #564", async function () {
1708
+ const element = document.getElementById("test-patch");
1709
+ const first = document.createElement("span");
1710
+ const replacement = document.createElement("span");
1711
+ first.dataset.key = "same";
1712
+ replacement.dataset.key = "same";
1713
+ let disposeCount = 0;
1714
+ addToObjectLink(
1715
+ first,
1716
+ customElementUpdaterLinkSymbol,
1717
+ new Set([{dispose() { disposeCount++; }}]),
1718
+ );
1719
+
1720
+ const updater = new Updater(element, {
1721
+ text: "",
1722
+ contentNode: document.createElement("div"),
1723
+ fragmentNode: document.createDocumentFragment(),
1724
+ itemsArray: [],
1725
+ keyedItems: [first],
1726
+ keyedDataItems: [],
1727
+ items: [],
1728
+ });
1729
+ updater.setCallback("getPatchKey", (value) => value?.dataset?.key);
1730
+ await updater.run();
1731
+
1732
+ updater.getSubject().keyedItems = [replacement];
1733
+ await new Promise((resolve) => setTimeout(resolve, 30));
1734
+
1735
+ expect(disposeCount).to.equal(1);
1736
+ expect(first.isConnected).to.equal(false);
1737
+ expect(document.getElementById("patch-keyed").firstChild).to.equal(replacement);
1738
+ });
1739
+
1668
1740
  it("should remove stale keyed nodes and keep surviving node identity", function (done) {
1669
1741
  let element = document.getElementById("test-patch");
1670
1742
  const a = document.createElement("span");
@@ -2620,6 +2692,52 @@ describe("DOM", function () {
2620
2692
  });
2621
2693
  });
2622
2694
 
2695
+ describe("Updater binding correctness issue #563", function () {
2696
+ it("should ignore unchecked radios during retrieve", function () {
2697
+ const mocks = document.getElementById("mocks");
2698
+ mocks.innerHTML = `
2699
+ <input type="radio" value="selected" checked data-monster-bind="path:choice">
2700
+ <input type="radio" value="unchecked" data-monster-bind="path:choice">
2701
+ `;
2702
+
2703
+ const updater = new Updater(mocks, {choice: "initial"});
2704
+ updater.retrieve();
2705
+
2706
+ expect(updater.getSubject().choice).to.equal("selected");
2707
+ });
2708
+
2709
+ it("should not change select state for non-value attributes", async function () {
2710
+ const mocks = document.getElementById("mocks");
2711
+ mocks.innerHTML = `
2712
+ <select id="binding-select" data-monster-attributes="title path:title">
2713
+ <option value="a">a</option>
2714
+ <option value="b" selected>b</option>
2715
+ </select>
2716
+ `;
2717
+
2718
+ const updater = new Updater(mocks, {title: "label"});
2719
+ await updater.run();
2720
+
2721
+ const select = document.getElementById("binding-select");
2722
+ expect(select.title).to.equal("label");
2723
+ expect(select.value).to.equal("b");
2724
+ });
2725
+
2726
+ it("should evaluate static attribute and property commands", async function () {
2727
+ const mocks = document.getElementById("mocks");
2728
+ mocks.innerHTML = `
2729
+ <div id="static-attribute" data-monster-attributes="title static:hello"></div>
2730
+ <input id="static-property" data-monster-properties="value static:world">
2731
+ `;
2732
+
2733
+ const updater = new Updater(mocks, {trigger: true});
2734
+ await updater.run();
2735
+
2736
+ expect(document.getElementById("static-attribute").title).to.equal("hello");
2737
+ expect(document.getElementById("static-property").value).to.equal("world");
2738
+ });
2739
+ });
2740
+
2623
2741
  describe("Updater pipe failure recovery issue #498", function () {
2624
2742
  const waitForUpdate = () => new Promise((resolve) => setTimeout(resolve, 20));
2625
2743
 
@@ -241,6 +241,40 @@ describe('ProxyObserver', function () {
241
241
  proxy.attachObserver(observer);
242
242
  delete proxy.getSubject().a;
243
243
  });
244
+
245
+ it('should ignore missing and inherited deletes without notifying issue #570', async function () {
246
+ const prototype = {inherited: true};
247
+ const value = Object.create(prototype);
248
+ const proxy = new ProxyObserver(value);
249
+ let notifications = 0;
250
+ proxy.attachObserver(new Observer(function () {
251
+ notifications++;
252
+ }));
253
+
254
+ expect(() => delete proxy.getSubject().missing).to.not.throw();
255
+ expect(() => delete proxy.getSubject().inherited).to.not.throw();
256
+ await new Promise((resolve) => setTimeout(resolve, 0));
257
+
258
+ expect(proxy.getSubject().inherited).to.equal(true);
259
+ expect(notifications).to.equal(0);
260
+ });
261
+
262
+ it('should report failed non-configurable deletes without notifying issue #570', async function () {
263
+ const value = {};
264
+ Object.defineProperty(value, 'fixed', {
265
+ configurable: false,
266
+ value: true,
267
+ });
268
+ const proxy = new ProxyObserver(value);
269
+ let notifications = 0;
270
+ proxy.attachObserver(new Observer(function () {
271
+ notifications++;
272
+ }));
273
+
274
+ expect(Reflect.deleteProperty(proxy.getSubject(), 'fixed')).to.equal(false);
275
+ await new Promise((resolve) => setTimeout(resolve, 0));
276
+ expect(notifications).to.equal(0);
277
+ });
244
278
  });
245
279
 
246
280
  describe('symbol properties', function () {
@@ -342,6 +376,47 @@ describe('ProxyObserver', function () {
342
376
  expect(proxy.getSubject().map).to.equal(replacement);
343
377
  expect(notifications).to.equal(1);
344
378
  });
379
+
380
+ it('should obey frozen-property proxy invariants issue #570', function () {
381
+ const nested = {value: true};
382
+ const frozen = Object.freeze({nested});
383
+ const subject = new ProxyObserver({frozen}).getSubject();
384
+
385
+ expect(() => subject.frozen.nested).to.not.throw();
386
+ expect(subject.frozen.nested).to.equal(nested);
387
+ });
388
+
389
+ it('should preserve confirmed internal-slot native objects issue #570', async function () {
390
+ const weakKey = {};
391
+ const regexp = /monster/g;
392
+ const buffer = new ArrayBuffer(8);
393
+ const view = new DataView(buffer);
394
+ const typed = new Uint8Array(buffer);
395
+ const weakMap = new WeakMap([[weakKey, 'value']]);
396
+ const weakSet = new WeakSet([weakKey]);
397
+ const promise = Promise.resolve('resolved');
398
+ const subject = new ProxyObserver({
399
+ regexp,
400
+ buffer,
401
+ view,
402
+ typed,
403
+ weakMap,
404
+ weakSet,
405
+ promise,
406
+ }).getSubject();
407
+
408
+ expect(subject.regexp).to.equal(regexp);
409
+ expect(subject.regexp.test('monster')).to.equal(true);
410
+ expect(subject.buffer).to.equal(buffer);
411
+ expect(subject.buffer.byteLength).to.equal(8);
412
+ expect(subject.view).to.equal(view);
413
+ expect(subject.view.byteLength).to.equal(8);
414
+ expect(subject.typed).to.equal(typed);
415
+ expect(subject.typed.length).to.equal(8);
416
+ expect(subject.weakMap.get(weakKey)).to.equal('value');
417
+ expect(subject.weakSet.has(weakKey)).to.equal(true);
418
+ expect(await subject.promise).to.equal('resolved');
419
+ });
345
420
  });
346
421
 
347
422
  })
@@ -12,7 +12,7 @@ describe('Processing', function () {
12
12
  let t = +new Date();
13
13
 
14
14
  const desiredDelay = 200;
15
- this.timeout(desiredDelay+100);
15
+ this.timeout(2000);
16
16
 
17
17
  // delay 12
18
18
  new Processing((v) => {
@@ -33,7 +33,7 @@ describe('Processing', function () {
33
33
  expect(counter).to.be.equal(4);
34
34
  expect(v === 'test').to.be.true;
35
35
  // check delay
36
- expect(+new Date() > t + desiredDelay+1).to.be.true;
36
+ expect(+new Date() >= t + desiredDelay).to.be.true;
37
37
  } catch (e) {
38
38
  return done(e);
39
39
  }
@@ -107,4 +107,4 @@ describe('Processing', function () {
107
107
 
108
108
  });
109
109
 
110
- });
110
+ });