@schukai/monster 4.151.0 → 4.152.0

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.","files":["source","test/cases","test/util","CHANGELOG.md"],"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.151.0"}
1
+ {"author":"Volker Schukai","dependencies":{"@floating-ui/dom":"^1.7.6"},"description":"Monster is a simple library for creating fast, robust and lightweight websites.","files":["source","test/cases","test/util","CHANGELOG.md"],"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.152.0"}
@@ -2206,6 +2206,9 @@ function fetchIt(url, controlOptions) {
2206
2206
  try {
2207
2207
  importOptionsIntern.call(self, map);
2208
2208
  processAndApplyPaginationData.call(self, map);
2209
+ if (this.getOption("options", []).length > 0) {
2210
+ removeErrorAttribute(this, "No options available.");
2211
+ }
2209
2212
  } catch (e) {
2210
2213
  setStatusOrRemoveBadges.call(this, "error");
2211
2214
  reject(e);
@@ -14,6 +14,7 @@
14
14
 
15
15
  import { instanceSymbol } from "../../constants.mjs";
16
16
  import { registerCustomElement } from "../../dom/customelement.mjs";
17
+ import { fireCustomEvent } from "../../dom/events.mjs";
17
18
  import { isInteger } from "../../types/is.mjs";
18
19
  import { validateInstance, validateString } from "../../types/validate.mjs";
19
20
  import { Button } from "./button.mjs";
@@ -33,6 +34,7 @@ export { StateButton };
33
34
  * @since 1.5.0
34
35
  * @copyright Volker Schukai
35
36
  * @summary A beautiful button with icons
37
+ * @fires monster-state-changed - Fired when the visual state changes
36
38
  */
37
39
  class StateButton extends Button {
38
40
  /**
@@ -111,7 +113,10 @@ class StateButton extends Button {
111
113
  throw new Error("state not found");
112
114
  }
113
115
 
114
- this.setOption("current", validateInstance(obj, State));
116
+ const previousState = this.getState()?.state ?? "stateless";
117
+ const currentState = validateInstance(obj, State);
118
+ this.setOption("current", currentState);
119
+ dispatchStateChanged.call(this, previousState, currentState.state);
115
120
 
116
121
  if (isInteger(timeout) && timeout > 0) {
117
122
  this[timeoutSymbol] = setTimeout(() => {
@@ -128,7 +133,10 @@ class StateButton extends Button {
128
133
  * @return {StateButton}
129
134
  */
130
135
  removeState() {
131
- this.setOption("current", getStateInstanceFor("stateless"));
136
+ const previousState = this.getState()?.state ?? "stateless";
137
+ const currentState = getStateInstanceFor("stateless");
138
+ this.setOption("current", currentState);
139
+ dispatchStateChanged.call(this, previousState, currentState.state);
132
140
  return this;
133
141
  }
134
142
 
@@ -156,6 +164,23 @@ class StateButton extends Button {
156
164
  }
157
165
  }
158
166
 
167
+ /**
168
+ * @private
169
+ * @param {string} previousState
170
+ * @param {string} state
171
+ * @return {void}
172
+ */
173
+ function dispatchStateChanged(previousState, state) {
174
+ if (previousState === state) {
175
+ return;
176
+ }
177
+
178
+ fireCustomEvent(this, "monster-state-changed", {
179
+ state,
180
+ previousState,
181
+ });
182
+ }
183
+
159
184
  /**
160
185
  * @private
161
186
  * @return {string}
@@ -131,6 +131,40 @@ describe("MessageStateButton", function () {
131
131
  });
132
132
  });
133
133
 
134
+ describe("state changes", function () {
135
+ afterEach(() => {
136
+ let mocks = document.getElementById("mocks");
137
+ mocks.innerHTML = "";
138
+ });
139
+
140
+ it("should expose inner state transitions on the public host", function (done) {
141
+ let mocks = document.getElementById("mocks");
142
+ const button = document.createElement("monster-message-state-button");
143
+ const events = [];
144
+ button.innerHTML = "Save";
145
+ button.addEventListener("monster-state-changed", (event) => {
146
+ events.push(event);
147
+ });
148
+ mocks.appendChild(button);
149
+
150
+ setTimeout(() => {
151
+ try {
152
+ button.setState("successful");
153
+
154
+ expect(events).to.have.length(1);
155
+ expect(events[0].target).to.equal(button);
156
+ expect(events[0].detail).to.deep.equal({
157
+ state: "successful",
158
+ previousState: "stateless",
159
+ });
160
+ done();
161
+ } catch (e) {
162
+ done(e);
163
+ }
164
+ }, 0);
165
+ });
166
+ });
167
+
134
168
  describe("lifecycle safety", function () {
135
169
  afterEach(() => {
136
170
  let mocks = document.getElementById("mocks");
@@ -124,7 +124,52 @@ describe('StateButton', function () {
124
124
 
125
125
 
126
126
  });
127
+
128
+ it('should emit state transitions without duplicate events', function () {
129
+ let mocks = document.getElementById('mocks');
130
+ const button = document.createElement('monster-state-button');
131
+ const events = [];
132
+ button.addEventListener('monster-state-changed', event => events.push(event));
133
+ mocks.appendChild(button);
134
+
135
+ button.setState('activity');
136
+ button.setState('activity');
137
+ button.setState('successful');
138
+ button.removeState();
139
+ button.removeState();
140
+
141
+ expect(events).to.have.length(3);
142
+ expect(events.map(event => event.detail)).to.deep.equal([
143
+ {state: 'activity', previousState: 'stateless'},
144
+ {state: 'successful', previousState: 'activity'},
145
+ {state: 'stateless', previousState: 'successful'},
146
+ ]);
147
+ expect(events.every(event => event.bubbles)).to.be.true;
148
+ expect(events.every(event => event.composed)).to.be.true;
149
+ });
150
+
151
+ it('should emit the automatic timeout transition', function (done) {
152
+ let mocks = document.getElementById('mocks');
153
+ const button = document.createElement('monster-state-button');
154
+ const details = [];
155
+ button.addEventListener('monster-state-changed', event => details.push(event.detail));
156
+ mocks.appendChild(button);
157
+
158
+ button.setState('activity', 10);
159
+
160
+ setTimeout(() => {
161
+ try {
162
+ expect(details).to.deep.equal([
163
+ {state: 'activity', previousState: 'stateless'},
164
+ {state: 'stateless', previousState: 'activity'},
165
+ ]);
166
+ done();
167
+ } catch (e) {
168
+ done(e);
169
+ }
170
+ }, 30);
171
+ });
127
172
  });
128
173
 
129
174
 
130
- });
175
+ });