@govuk-one-login/frontend-ui 2.0.0 → 3.0.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.
Files changed (34) hide show
  1. package/README.md +37 -0
  2. package/build/all.css +1 -1
  3. package/build/cjs/backend/index.cjs +14 -2
  4. package/build/cjs/backend/index.d.cts +10 -0
  5. package/build/cjs/backend/index.d.ts +10 -0
  6. package/build/cjs/backend/index.d.ts.map +1 -1
  7. package/build/cjs/frontend/index.cjs +225 -268
  8. package/build/cjs/frontend/index.d.cts +2 -1
  9. package/build/cjs/frontend/index.d.ts +2 -1
  10. package/build/cjs/frontend/index.d.ts.map +1 -1
  11. package/build/cjs/frontend/spinner/__tests__/spinner.test.d.ts +0 -4
  12. package/build/cjs/frontend/spinner/__tests__/spinner.test.d.ts.map +1 -1
  13. package/build/cjs/frontend/spinner/spinner.d.ts +54 -84
  14. package/build/cjs/frontend/spinner/spinner.d.ts.map +1 -1
  15. package/build/components/_all.scss +1 -0
  16. package/build/components/progress-button/_index.scss +44 -0
  17. package/build/components/progress-button/macro.njk +2 -0
  18. package/build/components/progress-button/progress-button.yaml +13 -0
  19. package/build/components/progress-button/template.njk +120 -0
  20. package/build/components/spinner/README.md +70 -52
  21. package/build/components/spinner/_index.scss +2 -11
  22. package/build/components/spinner/template.njk +15 -8
  23. package/build/esm/backend/index.d.ts +10 -0
  24. package/build/esm/backend/index.d.ts.map +1 -1
  25. package/build/esm/backend/index.js +14 -2
  26. package/build/esm/frontend/index.d.ts +2 -1
  27. package/build/esm/frontend/index.d.ts.map +1 -1
  28. package/build/esm/frontend/index.js +226 -269
  29. package/build/esm/frontend/spinner/__tests__/spinner.test.d.ts +0 -4
  30. package/build/esm/frontend/spinner/__tests__/spinner.test.d.ts.map +1 -1
  31. package/build/esm/frontend/spinner/spinner.d.ts +54 -84
  32. package/build/esm/frontend/spinner/spinner.d.ts.map +1 -1
  33. package/package.json +1 -1
  34. package/build/components/spinner/api.njk +0 -27
@@ -1,173 +1,246 @@
1
1
  'use strict';
2
2
 
3
- function useSpinner() {
4
- const element = document.getElementById("spinner-container");
5
- if (element) {
6
- const spinner = new Spinner(element);
7
- spinner.init();
3
+ async function useSpinner(containerId, pollingFunction, successFunction, errorFunction) {
4
+ const element = document.getElementById(containerId);
5
+ if (element && element instanceof HTMLDivElement) {
6
+ let spinner;
7
+ try {
8
+ spinner = new Spinner(element, pollingFunction, successFunction, errorFunction);
9
+ }
10
+ catch (e) {
11
+ const errorText = document.createElement("p");
12
+ errorText.textContent = "Error configuring spinner: " + e;
13
+ element.replaceChildren(errorText);
14
+ return;
15
+ }
16
+ await spinner.init();
8
17
  }
9
18
  else {
10
- console.warn("Attempting to initiate a spinner on a page with no '#spinner-container' element.");
19
+ console.error(`Attempting to initiate a spinner on a page with no '#${containerId}' div element.`);
11
20
  }
12
21
  }
22
+ exports.PollResult = void 0;
23
+ (function (PollResult) {
24
+ PollResult[PollResult["Success"] = 0] = "Success";
25
+ PollResult[PollResult["Failure"] = 1] = "Failure";
26
+ PollResult[PollResult["Pending"] = 2] = "Pending";
27
+ })(exports.PollResult || (exports.PollResult = {}));
28
+ var SpinnerState;
29
+ (function (SpinnerState) {
30
+ SpinnerState[SpinnerState["Waiting"] = 0] = "Waiting";
31
+ SpinnerState[SpinnerState["LongWaiting"] = 1] = "LongWaiting";
32
+ SpinnerState[SpinnerState["Error"] = 2] = "Error";
33
+ SpinnerState[SpinnerState["Complete"] = 3] = "Complete";
34
+ })(SpinnerState || (SpinnerState = {}));
13
35
  class Spinner {
14
- reflectLongWait() {
15
- if (this.state.spinnerState !== "ready") {
16
- this.state.spinnerStateText = this.content.longWait.spinnerStateText;
36
+ constructor(domContainer, pollingFunction, onSuccess, onError) {
37
+ this.state = SpinnerState.Waiting;
38
+ this.handleAbort = () => {
39
+ this.abortController.abort();
40
+ };
41
+ this.initTimer = (initTime) => {
42
+ this.updateDomTimer = setInterval(() => {
43
+ this.updateStateAccordingToTimeElapsed(initTime);
44
+ this.updateDom();
45
+ }, this.config.msBetweenDomUpdate);
46
+ };
47
+ this.reflectSuccess = () => {
48
+ this.state = SpinnerState.Complete;
49
+ sessionStorage.removeItem("spinnerInitTime");
50
+ };
51
+ this.reflectError = () => {
52
+ this.state = SpinnerState.Error;
53
+ sessionStorage.removeItem("spinnerInitTime");
54
+ this.abortController.abort();
55
+ };
56
+ this.updateStateAccordingToTimeElapsed = (initTime) => {
57
+ const elapsedMilliseconds = Date.now() - initTime;
58
+ if (this.hasCompleted()) {
59
+ // If we've already finished waiting then there's no need to update again.
60
+ return;
61
+ }
62
+ if (elapsedMilliseconds >= this.config.msBeforeAbort) {
63
+ this.reflectError();
64
+ }
65
+ else if (elapsedMilliseconds >= this.config.msBeforeInformingOfLongWait) {
66
+ this.reflectLongWait();
67
+ }
68
+ };
69
+ this.updateDom = () => {
70
+ if (this.hasCompleted()) {
71
+ // We've reached an end state so stop updating the DOM after this
72
+ clearInterval(this.updateDomTimer);
73
+ }
74
+ if (this.displayState === this.state) {
75
+ // No need to update anything
76
+ return;
77
+ }
78
+ this.displayState = this.state;
79
+ const newElementsToDisplay = [];
80
+ switch (this.displayState) {
81
+ case SpinnerState.Waiting:
82
+ newElementsToDisplay.push(this.createSpinnerElement(""));
83
+ this.cloneAndAddIfExists(newElementsToDisplay, this.waitContent);
84
+ break;
85
+ case SpinnerState.LongWaiting:
86
+ newElementsToDisplay.push(this.createSpinnerElement(""));
87
+ this.cloneAndAddIfExists(newElementsToDisplay, this.longWaitContent);
88
+ break;
89
+ case SpinnerState.Complete:
90
+ newElementsToDisplay.push(this.createSpinnerElement("spinner__finished"));
91
+ this.cloneAndAddIfExists(newElementsToDisplay, this.successContent);
92
+ break;
93
+ case SpinnerState.Error:
94
+ if (!this.config.hideSpinnerOnError) {
95
+ newElementsToDisplay.push(this.createSpinnerElement("spinner__finished"));
96
+ }
97
+ this.cloneAndAddIfExists(newElementsToDisplay, this.errorContent);
98
+ break;
99
+ }
100
+ this.visibleElementsContainer.replaceChildren(...newElementsToDisplay);
101
+ if (this.displayState === SpinnerState.Complete) {
102
+ if (this.onSuccess) {
103
+ this.onSuccess();
104
+ }
105
+ this.updateAriaAlert(this.config.ariaAlertCompletionText);
106
+ }
107
+ if (this.displayState === SpinnerState.Error && !!this.onError) {
108
+ this.onError();
109
+ }
110
+ };
111
+ this.container = domContainer;
112
+ this.pollingFunction = pollingFunction;
113
+ if (!pollingFunction) {
114
+ throw new Error("Polling function must be provided");
17
115
  }
116
+ this.onSuccess = onSuccess;
117
+ this.onError = onError;
118
+ this.noJsContent = this.getElementOrThrow("no-js-content");
119
+ this.noJsContent.style.display = "none";
120
+ this.waitContent =
121
+ this.container.querySelector("#wait-content") || undefined;
122
+ this.longWaitContent =
123
+ this.container.querySelector("#long-wait-content") || undefined;
124
+ this.successContent =
125
+ this.container.querySelector("#success-content") || undefined;
126
+ this.errorContent =
127
+ this.container.querySelector("#error-content") || undefined;
128
+ if (!this.successContent && !onSuccess) {
129
+ throw new Error("One of success-content or successFunction must be provided");
130
+ }
131
+ if (!this.errorContent && !onError) {
132
+ throw new Error("One of error-content or errorFunction must be provided");
133
+ }
134
+ this.config = this.getConfig(this.container);
135
+ this.visibleElementsContainer = document.createElement("div");
136
+ this.container.appendChild(this.visibleElementsContainer);
137
+ this.ariaLiveContainer = this.createAriaLiveContainer();
138
+ this.container.appendChild(this.ariaLiveContainer);
139
+ this.abortController = this.createAbortController();
18
140
  }
19
- initialiseState() {
20
- if (this.domRequirementsMet) {
21
- this.state = {
22
- heading: this.content.initial.heading,
23
- spinnerStateText: this.content.initial.spinnerStateText,
24
- spinnerState: this.content.initial.spinnerState,
25
- buttonDisabled: true,
26
- ariaButtonEnabledMessage: "",
27
- done: false,
28
- error: false,
29
- virtualDom: [],
30
- };
141
+ async init() {
142
+ const initTime = this.getInitTime();
143
+ this.updateStateAccordingToTimeElapsed(initTime);
144
+ this.updateDom();
145
+ this.initTimer(initTime);
146
+ await this.callPollingFunction(initTime);
147
+ }
148
+ getElementOrThrow(elementId) {
149
+ const element = this.container.querySelector(`#${elementId}`);
150
+ if (element === null || !(element instanceof HTMLElement)) {
151
+ throw new Error(`HTML Element with id ${elementId} must be provided`);
31
152
  }
153
+ return element;
32
154
  }
33
- initialiseContent(element) {
34
- var _a;
35
- function throwIfMissing(data) {
36
- if (data === undefined || data === null) {
37
- throw new Error("Missing required data");
38
- }
39
- return data;
155
+ getConfig(element) {
156
+ return {
157
+ msBeforeInformingOfLongWait: element.dataset.msBeforeInformingOfLongWait
158
+ ? parseInt(element.dataset.msBeforeInformingOfLongWait)
159
+ : 5000,
160
+ msBeforeAbort: element.dataset.msBeforeAbort
161
+ ? parseInt(element.dataset.msBeforeAbort)
162
+ : 30000,
163
+ msBetweenRequests: element.dataset.msBetweenRequests
164
+ ? parseInt(element.dataset.msBetweenRequests)
165
+ : 2000,
166
+ msBetweenDomUpdate: element.dataset.msBetweenDomUpdate
167
+ ? parseInt(element.dataset.msBetweenDomUpdate)
168
+ : 1000,
169
+ ariaAlertCompletionText: element.dataset.ariaAlertCompletionText
170
+ ? element.dataset.ariaAlertCompletionText
171
+ : undefined,
172
+ hideSpinnerOnError: element.dataset.hideSpinnerOnError
173
+ ? (element.dataset.hideSpinnerOnError === 'true')
174
+ : false,
175
+ };
176
+ }
177
+ createAbortController() {
178
+ const abortController = new AbortController();
179
+ window.removeEventListener("beforeunload", this.handleAbort);
180
+ window.addEventListener("beforeunload", this.handleAbort);
181
+ return abortController;
182
+ }
183
+ getInitTime() {
184
+ const storedSpinnerInitTime = sessionStorage.getItem("spinnerInitTime");
185
+ let spinnerInitTime;
186
+ if (storedSpinnerInitTime === null) {
187
+ spinnerInitTime = Date.now();
188
+ sessionStorage.setItem("spinnerInitTime", spinnerInitTime.toString());
40
189
  }
41
- try {
42
- this.content = {
43
- initial: {
44
- heading: throwIfMissing(element.dataset.initialHeading),
45
- spinnerStateText: throwIfMissing(element.dataset.initialSpinnerstatetext),
46
- spinnerState: throwIfMissing(element.dataset.initialSpinnerstate),
47
- },
48
- error: {
49
- heading: throwIfMissing(element.dataset.errorHeading),
50
- messageText: throwIfMissing(element.dataset.errorMessagetext),
51
- whatYouCanDo: {
52
- heading: throwIfMissing(element.dataset.errorWhatyoucandoHeading),
53
- message: {
54
- text1: throwIfMissing(element.dataset.errorWhatyoucandoMessageText1),
55
- link: {
56
- href: throwIfMissing(element.dataset.errorWhatyoucandoMessageLinkHref),
57
- text: throwIfMissing(element.dataset.errorWhatyoucandoMessageLinkText),
58
- },
59
- text2: throwIfMissing(element.dataset.errorWhatyoucandoMessageText2),
60
- },
61
- },
62
- },
63
- complete: {
64
- spinnerState: throwIfMissing(element.dataset.completeSpinnerstate),
65
- },
66
- longWait: {
67
- spinnerStateText: throwIfMissing(element.dataset.longwaitSpinnerstatetext),
68
- },
69
- continueButton: {
70
- text: (_a = element.dataset.continuebuttonText) !== null && _a !== void 0 ? _a : "Continue",
71
- },
72
- };
73
- this.config = {
74
- apiUrl: element.dataset.apiUrl || this.config.apiUrl,
75
- ariaButtonEnabledMessage: throwIfMissing(element.dataset.ariaButtonEnabledMessage),
76
- msBeforeInformingOfLongWait: element.dataset.msBeforeInformingOfLongWait
77
- ? parseInt(element.dataset.msBeforeInformingOfLongWait)
78
- : this.config.msBeforeInformingOfLongWait,
79
- msBeforeAbort: element.dataset.msBeforeAbort
80
- ? parseInt(element.dataset.msBeforeAbort)
81
- : this.config.msBeforeAbort,
82
- msBetweenRequests: element.dataset.msBetweenRequests
83
- ? parseInt(element.dataset.msBetweenRequests)
84
- : this.config.msBetweenRequests,
85
- msBetweenDomUpdate: element.dataset.msBetweenDomUpdate
86
- ? parseInt(element.dataset.msBetweenDomUpdate)
87
- : this.config.msBetweenDomUpdate,
88
- };
89
- this.domRequirementsMet = true;
190
+ else {
191
+ spinnerInitTime = parseInt(storedSpinnerInitTime, 10);
90
192
  }
91
- catch (e) {
92
- this.domRequirementsMet = false;
193
+ return spinnerInitTime;
194
+ }
195
+ hasCompleted() {
196
+ return (this.state === SpinnerState.Error || this.state === SpinnerState.Complete);
197
+ }
198
+ reflectLongWait() {
199
+ if (!this.hasCompleted()) {
200
+ this.state = SpinnerState.LongWaiting;
93
201
  }
94
202
  }
95
- createVirtualDom() {
96
- const domInitialState = [
97
- {
98
- nodeName: "h1",
99
- text: this.state.heading,
100
- classes: ["govuk-heading-l"],
101
- },
102
- {
103
- nodeName: "div",
104
- id: "spinner",
105
- classes: [
106
- "spinner",
107
- "spinner__pending",
108
- "centre",
109
- this.state.spinnerState,
110
- ],
111
- },
112
- {
113
- nodeName: "p",
114
- text: this.state.spinnerStateText,
115
- classes: ["centre", "spinner-state-text", "govuk-body"],
116
- },
117
- {
118
- nodeName: "button",
119
- text: this.content.continueButton.text,
120
- buttonDisabled: this.state.buttonDisabled,
121
- classes: ["govuk-button", "govuk-!-margin-top-4"],
122
- },
123
- ];
124
- const domErrorState = [
125
- {
126
- nodeName: "h1",
127
- text: this.state.heading,
128
- classes: ["govuk-heading-l"],
129
- },
130
- {
131
- nodeName: "p",
132
- text: this.state.messageText,
133
- classes: ["govuk-body"],
134
- },
135
- {
136
- nodeName: "h2",
137
- text: this.content.error.whatYouCanDo.heading,
138
- classes: ["govuk-heading-m"],
139
- },
140
- {
141
- nodeName: "p",
142
- innerHTML: `${this.content.error.whatYouCanDo.message.text1}<a href="${this.content.error.whatYouCanDo.message.link.href}">${this.content.error.whatYouCanDo.message.link.text}</a>${this.content.error.whatYouCanDo.message.text2}`,
143
- classes: ["govuk-body"],
144
- },
145
- ];
146
- return this.state.error ? domErrorState : domInitialState;
203
+ createSpinnerElement(spinnerStateClass) {
204
+ const spinner = document.createElement("div");
205
+ spinner.id = "spinner";
206
+ spinner.classList.add("spinner", "centre");
207
+ if (spinnerStateClass) {
208
+ spinner.classList.add(spinnerStateClass);
209
+ }
210
+ return spinner;
147
211
  }
148
- async requestIDProcessingStatus(initTime) {
149
- const signal = this.abortController.signal;
150
- await fetch(this.config.apiUrl, { signal })
151
- .then((response) => response.json())
152
- .then((data) => {
153
- if (data.status === "COMPLETED" || data.status === "INTERVENTION") {
154
- this.reflectCompletion();
212
+ cloneAndAddIfExists(list, element) {
213
+ if (element) {
214
+ const cloned = element.cloneNode(true);
215
+ cloned.style.display = "";
216
+ cloned.removeAttribute("id");
217
+ list.push(cloned);
218
+ }
219
+ }
220
+ async callPollingFunction(initTime) {
221
+ if (Date.now() - initTime >= this.config.msBeforeAbort) {
222
+ return;
223
+ }
224
+ await this.pollingFunction(this.abortController.signal)
225
+ .then((response) => {
226
+ if (response === exports.PollResult.Success) {
227
+ this.reflectSuccess();
155
228
  }
156
- else if (data.status === "ERROR") {
229
+ else if (response === exports.PollResult.Failure) {
157
230
  this.reflectError();
158
231
  }
159
- else if (this.notInErrorOrDoneState()) {
232
+ else if (!this.hasCompleted()) {
160
233
  setTimeout(async () => {
161
234
  if (Date.now() - initTime >= this.config.msBeforeAbort) {
162
235
  return;
163
236
  }
164
- await this.requestIDProcessingStatus(initTime);
237
+ await this.callPollingFunction(initTime);
165
238
  }, this.config.msBetweenRequests);
166
239
  }
167
240
  })
168
241
  .catch((error) => {
169
242
  if (error.name !== "AbortError") {
170
- console.error("Error in requestIDProcessingStatus:", error);
243
+ console.error("Error in polling function: ", error);
171
244
  this.reflectError();
172
245
  }
173
246
  })
@@ -175,139 +248,23 @@ class Spinner {
175
248
  this.updateDom();
176
249
  });
177
250
  }
178
- getInitTime() {
179
- const storedSpinnerInitTime = sessionStorage.getItem("spinnerInitTime");
180
- let spinnerInitTime;
181
- if (storedSpinnerInitTime === null) {
182
- spinnerInitTime = Date.now();
183
- sessionStorage.setItem("spinnerInitTime", spinnerInitTime.toString());
184
- }
185
- else {
186
- spinnerInitTime = parseInt(storedSpinnerInitTime, 10);
187
- }
188
- return spinnerInitTime;
189
- }
190
- init() {
191
- if (this.domRequirementsMet) {
192
- const initTime = this.getInitTime();
193
- this.initTimer(initTime);
194
- this.initialiseContainers();
195
- this.updateDom();
196
- this.requestIDProcessingStatus(initTime);
197
- }
251
+ createAriaLiveContainer() {
252
+ // For the Aria alert to work reliably we need to create its container once and then update the contents
253
+ // https://tetralogical.com/blog/2024/05/01/why-are-my-live-regions-not-working/
254
+ const container = document.createElement("div");
255
+ container.setAttribute("aria-live", "assertive");
256
+ container.classList.add("govuk-visually-hidden");
257
+ return container;
198
258
  }
199
- constructor(domContainer) {
200
- this.config = {
201
- apiUrl: "/prove-identity-status",
202
- msBeforeInformingOfLongWait: 5000,
203
- msBeforeAbort: 25000,
204
- msBetweenRequests: 1000,
205
- msBetweenDomUpdate: 2000,
206
- };
207
- this.notInErrorOrDoneState = () => {
208
- return !(this.state.done || this.state.error);
209
- };
210
- this.reflectCompletion = () => {
211
- this.state.spinnerState = "spinner__ready";
212
- this.state.spinnerStateText = this.content.complete.spinnerState;
213
- this.state.buttonDisabled = false;
214
- this.state.ariaButtonEnabledMessage =
215
- this.content.complete.ariaButtonEnabledMessage;
216
- this.state.done = true;
217
- sessionStorage.removeItem("spinnerInitTime");
218
- };
219
- this.reflectError = () => {
220
- this.state.heading = this.content.error.heading;
221
- this.state.messageText = this.content.error.messageText;
222
- this.state.spinnerState = "spinner__failed";
223
- this.state.done = true;
224
- this.state.error = true;
225
- sessionStorage.removeItem("spinnerInitTime");
226
- this.abortController.abort();
227
- };
228
- this.updateAccordingToTimeElapsed = (initTime) => {
229
- const elapsedMilliseconds = Date.now() - initTime;
230
- if (elapsedMilliseconds >= this.config.msBeforeAbort) {
231
- this.reflectError();
232
- }
233
- else if (elapsedMilliseconds >= this.config.msBeforeInformingOfLongWait) {
234
- this.reflectLongWait();
235
- }
236
- };
237
- this.vDomHasChanged = (currentVDom, nextVDom) => {
238
- return JSON.stringify(currentVDom) !== JSON.stringify(nextVDom);
239
- };
240
- this.convert = (node) => {
241
- const el = document.createElement(node.nodeName);
242
- if (node.text)
243
- el.textContent = node.text;
244
- if (node.innerHTML)
245
- el.innerHTML = node.innerHTML;
246
- if (node.id)
247
- el.id = node.id;
248
- if (node.classes)
249
- el.classList.add(...node.classes);
250
- if (node.buttonDisabled)
251
- el.setAttribute("disabled", `${node.buttonDisabled}`);
252
- return el;
253
- };
254
- this.updateAriaAlert = (messageText) => {
259
+ updateAriaAlert(messageText) {
260
+ if (messageText) {
255
261
  while (this.ariaLiveContainer.firstChild) {
256
262
  this.ariaLiveContainer.removeChild(this.ariaLiveContainer.firstChild);
257
263
  }
258
264
  /* Create new message and append it to the live region */
259
265
  const messageNode = document.createTextNode(messageText);
260
266
  this.ariaLiveContainer.appendChild(messageNode);
261
- };
262
- this.updateDom = () => {
263
- const vDomChanged = this.vDomHasChanged(this.state.virtualDom, this.createVirtualDom());
264
- if (vDomChanged) {
265
- document.title = this.state.heading;
266
- this.state.virtualDom = this.createVirtualDom();
267
- const elements = this.state.virtualDom.map(this.convert);
268
- this.spinnerContainer.replaceChildren(...elements);
269
- }
270
- if (this.state.error) {
271
- this.spinnerContainer.classList.add("spinner-container__error");
272
- }
273
- if (this.state.done) {
274
- clearInterval(this.updateDomTimer);
275
- }
276
- if (this.config.ariaButtonEnabledMessage &&
277
- this.state.ariaButtonEnabledMessage !== "") {
278
- this.updateAriaAlert(this.config.ariaButtonEnabledMessage);
279
- }
280
- };
281
- // For the Aria alert to work reliably we need to create its container once and then update the contents
282
- // https://tetralogical.com/blog/2024/05/01/why-are-my-live-regions-not-working/
283
- // So here we create a separate DOM element for the Aria live text that won't be touched when the spinner updates.
284
- this.initialiseContainers = () => {
285
- this.spinnerContainer = document.createElement("div");
286
- this.ariaLiveContainer = document.createElement("div");
287
- this.ariaLiveContainer.setAttribute("aria-live", "assertive");
288
- this.ariaLiveContainer.classList.add("govuk-visually-hidden");
289
- this.ariaLiveContainer.appendChild(document.createTextNode(""));
290
- this.container.replaceChildren(this.spinnerContainer, this.ariaLiveContainer);
291
- };
292
- this.initTimer = (initTime) => {
293
- this.updateAccordingToTimeElapsed(initTime);
294
- this.updateDomTimer = setInterval(() => {
295
- this.updateAccordingToTimeElapsed(initTime);
296
- this.updateDom();
297
- }, this.config.msBetweenDomUpdate);
298
- };
299
- this.handleAbort = () => {
300
- this.abortController.abort();
301
- };
302
- this.initialiseAbortController = () => {
303
- this.abortController = new AbortController();
304
- window.removeEventListener("beforeunload", this.handleAbort);
305
- window.addEventListener("beforeunload", this.handleAbort);
306
- };
307
- this.container = domContainer;
308
- this.initialiseContent(this.container);
309
- this.initialiseState();
310
- this.initialiseAbortController();
267
+ }
311
268
  }
312
269
  }
313
270
 
@@ -1,2 +1,3 @@
1
- export { useSpinner } from "./spinner/spinner";
1
+ export { useSpinner, PollResult } from "./spinner/spinner";
2
+ export type { PollingFunction } from "./spinner/spinner";
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -1,2 +1,3 @@
1
- export { useSpinner } from "./spinner/spinner";
1
+ export { useSpinner, PollResult } from "./spinner/spinner";
2
+ export type { PollingFunction } from "./spinner/spinner";
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../frontend-src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../frontend-src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC3D,YAAY,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC"}
@@ -1,6 +1,2 @@
1
1
  export declare function wait(ms: number): Promise<unknown>;
2
- export declare function waitUntil(conditionFn: Function, { timeout, interval }?: {
3
- timeout?: number | undefined;
4
- interval?: number | undefined;
5
- }): Promise<void>;
6
2
  //# sourceMappingURL=spinner.test.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"spinner.test.d.ts","sourceRoot":"","sources":["../../../../../frontend-src/spinner/__tests__/spinner.test.ts"],"names":[],"mappings":"AAEA,wBAAgB,IAAI,CAAC,EAAE,EAAE,MAAM,oBAE9B;AAED,wBAAsB,SAAS,CAC7B,WAAW,EAAE,QAAQ,EACrB,EAAE,OAAc,EAAE,QAAa,EAAE;;;CAAK,iBAcvC"}
1
+ {"version":3,"file":"spinner.test.d.ts","sourceRoot":"","sources":["../../../../../frontend-src/spinner/__tests__/spinner.test.ts"],"names":[],"mappings":"AAGA,wBAAgB,IAAI,CAAC,EAAE,EAAE,MAAM,oBAE9B"}