@schukai/monster 4.150.4 → 4.150.5

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.150.4"}
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.150.5"}
@@ -45,6 +45,7 @@ const currentPanelIndexSymbol = Symbol("currentPanelIndex");
45
45
  const optionObserverSymbol = Symbol("optionObserver");
46
46
  const idsSymbol = Symbol("ids");
47
47
  const finalRecordSymbol = Symbol("finalRecord");
48
+ const slottedElementsSymbol = Symbol("slottedElements");
48
49
 
49
50
  /**
50
51
  * A reusable wizard container that combines content panels with `monster-wizard-navigation`.
@@ -151,6 +152,7 @@ class Wizard extends CustomElement {
151
152
  panel: 0,
152
153
  group: 0,
153
154
  };
155
+ this[slottedElementsSymbol] = [];
154
156
 
155
157
  initControlReferences.call(this);
156
158
  initEventHandler.call(this);
@@ -170,6 +172,9 @@ class Wizard extends CustomElement {
170
172
  * @return {Wizard}
171
173
  */
172
174
  refresh() {
175
+ this[slottedElementsSymbol] = Array.from(
176
+ getSlottedElements.call(this) || [],
177
+ );
173
178
  const records = collectPanels.call(this);
174
179
  const groups = buildGroups.call(this, records);
175
180
 
@@ -388,6 +393,10 @@ function initControlReferences() {
388
393
 
389
394
  function initEventHandler() {
390
395
  this[slotElementSymbol]?.addEventListener("slotchange", () => {
396
+ const elements = Array.from(getSlottedElements.call(this) || []);
397
+ if (haveSameElements(this[slottedElementsSymbol], elements)) {
398
+ return;
399
+ }
391
400
  this.refresh();
392
401
  });
393
402
 
@@ -449,6 +458,13 @@ function initEventHandler() {
449
458
  });
450
459
  }
451
460
 
461
+ function haveSameElements(currentElements, nextElements) {
462
+ return (
463
+ currentElements.length === nextElements.length &&
464
+ currentElements.every((element, index) => element === nextElements[index])
465
+ );
466
+ }
467
+
452
468
  function initOptionObserver() {
453
469
  if (this[optionObserverSymbol]) {
454
470
  return;
@@ -19,6 +19,7 @@ const currentStepIndexSymbol = Symbol("currentStepIndex");
19
19
  const stepsSymbol = Symbol("steps");
20
20
  const isTransitioningSymbol = Symbol("isTransitioning");
21
21
  const currentSubStepIndexSymbol = Symbol("currentSubStepIndex");
22
+ const activationRequestSymbol = Symbol("activationRequest");
22
23
 
23
24
  /**
24
25
  * A WizardNavigation component to display progress and allow navigation through a series of steps,
@@ -45,6 +46,7 @@ class WizardNavigation extends CustomElement {
45
46
  this[isTransitioningSymbol] = false;
46
47
  this[currentStepIndexSymbol] = -1;
47
48
  this[currentSubStepIndexSymbol] = -1;
49
+ this[activationRequestSymbol] = 0;
48
50
 
49
51
  initControlReferences.call(this);
50
52
  initEventHandler.call(this);
@@ -186,7 +188,11 @@ class WizardNavigation extends CustomElement {
186
188
  return;
187
189
  }
188
190
 
191
+ const request = ++this[activationRequestSymbol];
189
192
  await this.goToStep(mainIndex, options);
193
+ if (request !== this[activationRequestSymbol]) {
194
+ return;
195
+ }
190
196
 
191
197
  const stepsData = this[stepsSymbol];
192
198
  if (!stepsData || !stepsData[mainIndex]) return;
@@ -98,6 +98,24 @@ describe("Wizard", function () {
98
98
  ).to.equal(true);
99
99
  });
100
100
 
101
+ it("should ignore slot changes when the assigned panels are unchanged", async () => {
102
+ const mocks = document.getElementById("mocks");
103
+ mocks.innerHTML = html;
104
+
105
+ await new Promise((resolve) => setTimeout(resolve, 30));
106
+
107
+ const wizard = document.getElementById("wizard-under-test");
108
+ const slot = wizard.shadowRoot.querySelector("slot");
109
+ let refreshCount = 0;
110
+ wizard.addEventListener("monster-wizard-refresh", () => {
111
+ refreshCount++;
112
+ });
113
+
114
+ slot.dispatchEvent(new Event("slotchange"));
115
+
116
+ expect(refreshCount).to.equal(0);
117
+ });
118
+
101
119
  it("should use panel-specific validation callbacks before moving forward", async function () {
102
120
  const mocks = document.getElementById("mocks");
103
121
  mocks.innerHTML = html;
@@ -0,0 +1,49 @@
1
+ import * as chai from "chai";
2
+ import { initJSDOM } from "../../../util/jsdom.mjs";
3
+
4
+ const expect = chai.expect;
5
+
6
+ describe("WizardNavigation", () => {
7
+ before(async () => {
8
+ await initJSDOM();
9
+ await import(
10
+ "../../../../source/components/navigation/wizard-navigation.mjs"
11
+ );
12
+ });
13
+
14
+ afterEach(() => {
15
+ document.getElementById("mocks").innerHTML = "";
16
+ });
17
+
18
+ it("should only dispatch the latest concurrent substep activation", async () => {
19
+ const mocks = document.getElementById("mocks");
20
+ mocks.innerHTML = `
21
+ <monster-wizard-navigation id="wizard-navigation-under-test">
22
+ <ol class="wizard-steps">
23
+ <li class="step">
24
+ <span>Account</span>
25
+ <ul>
26
+ <li>Email</li>
27
+ <li>Password</li>
28
+ </ul>
29
+ </li>
30
+ </ol>
31
+ </monster-wizard-navigation>
32
+ `;
33
+
34
+ await new Promise((resolve) => setTimeout(resolve, 20));
35
+
36
+ const navigation = document.getElementById("wizard-navigation-under-test");
37
+ const substeps = [];
38
+ navigation.addEventListener("monster-wizard-substep-changed", (event) => {
39
+ substeps.push(event.detail.subIndex);
40
+ });
41
+
42
+ await Promise.all([
43
+ navigation.activate(0, 0, { force: true }),
44
+ navigation.activate(0, 1, { force: true }),
45
+ ]);
46
+
47
+ expect(substeps).to.deep.equal([1]);
48
+ });
49
+ });