@aurodesignsystem/auro-library 5.12.1 → 5.12.3

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 CHANGED
@@ -1,5 +1,27 @@
1
1
  # Semantic Release Automated Changelog
2
2
 
3
+ ## [5.12.3](https://github.com/AlaskaAirlines/auro-library/compare/v5.12.2...v5.12.3) (2026-04-30)
4
+
5
+
6
+ ### Bug Fixes
7
+
8
+ * **floatingUI:** restore bib transform ([65a236f](https://github.com/AlaskaAirlines/auro-library/commit/65a236f0419d4946b6cb194e1ea6e8af74cdf15e))
9
+ * **floatingUI:** set `role="application"` on bib Container to lock 3finger swipe ([88fa5ca](https://github.com/AlaskaAirlines/auro-library/commit/88fa5ca6875f91a301b4a26f2d195d47b7e0192a))
10
+ * **floatingUI:** use `aria-modal` instead of `role="application"` ([7a3dddc](https://github.com/AlaskaAirlines/auro-library/commit/7a3dddc2a42a6bc416ae5b35f0b9efc5aeb2998f))
11
+
12
+
13
+ ### Performance Improvements
14
+
15
+ * **floatingUI:** add queue to main track opened floatingUI activities ([662ce52](https://github.com/AlaskaAirlines/auro-library/commit/662ce52efc0b0b74db6512f7e6b2a60d64a3ab98))
16
+ * **floatingUI:** lock scroll for fullscreen mode ([6d425b6](https://github.com/AlaskaAirlines/auro-library/commit/6d425b64e66a2ec1519f098b5472580e29dcf6a7))
17
+
18
+ ## [5.12.2](https://github.com/AlaskaAirlines/auro-library/compare/v5.12.1...v5.12.2) (2026-04-09)
19
+
20
+
21
+ ### Performance Improvements
22
+
23
+ * add touch handler to floatingUI ([c89a29c](https://github.com/AlaskaAirlines/auro-library/commit/c89a29c5d9ce5945f52ee3e00f79a648e5da6ca7))
24
+
3
25
  ## [5.12.1](https://github.com/AlaskaAirlines/auro-library/compare/v5.12.0...v5.12.1) (2026-04-07)
4
26
 
5
27
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aurodesignsystem/auro-library",
3
- "version": "5.12.1",
3
+ "version": "5.12.3",
4
4
  "description": "This repository holds shared scripts, utilities, and workflows utilized across repositories along the Auro Design System.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -62,6 +62,36 @@ export default class AuroFloatingUI {
62
62
  }
63
63
  }
64
64
 
65
+ static openingQueue = [];
66
+
67
+ /**
68
+ * Returns the currently active floating UI instance that should be considered "on top".
69
+ * Prefers any globally tracked expanded dropdown or floater, falling back to the last entry in the local opening queue.
70
+ *
71
+ * This getter first checks the global document references for a visible floating UI and returns it if found.
72
+ * If the global reference is stale or not visible, it clears those references and instead returns the most recently opened instance from `openingQueue`, or `null` if none exist.
73
+ *
74
+ * Side effect: clears stale global refs so callers don't need a separate cleanup step.
75
+ */
76
+ static get topOpeningFloatingUI() {
77
+ const existedVisibleFloatingUI =
78
+ document.expandedAuroFormkitDropdown || document.expandedAuroFloater;
79
+ if (
80
+ existedVisibleFloatingUI &&
81
+ existedVisibleFloatingUI.element.isPopoverVisible
82
+ ) {
83
+ return existedVisibleFloatingUI;
84
+ }
85
+
86
+ // clear existedVisibleFloatingUI in case it's not flushed well when it hidden by other reasons (e.g. noToggle + click)
87
+ document.expandedAuroFormkitDropdown = null;
88
+ document.expandedAuroFloater = null;
89
+
90
+ return AuroFloatingUI.openingQueue.length > 0
91
+ ? AuroFloatingUI.openingQueue[AuroFloatingUI.openingQueue.length - 1]
92
+ : null;
93
+ }
94
+
65
95
  constructor(element, behavior) {
66
96
  this.element = element;
67
97
  this.behavior = behavior;
@@ -70,6 +100,7 @@ export default class AuroFloatingUI {
70
100
  this.focusHandler = null;
71
101
  this.clickHandler = null;
72
102
  this.keyDownHandler = null;
103
+ this.touchHandler = null;
73
104
 
74
105
  /**
75
106
  * @private
@@ -160,12 +191,16 @@ export default class AuroFloatingUI {
160
191
  `(max-width: ${breakpoint})`,
161
192
  ).matches;
162
193
 
163
- element.expanded = smallerThanBreakpoint;
164
- }
165
- if (element.nested) {
166
- return "cover";
194
+ this.element.expanded = smallerThanBreakpoint;
195
+
196
+ if (this.element.nested) {
197
+ return "cover";
198
+ }
199
+ return smallerThanBreakpoint || this.element.modal
200
+ ? "fullscreen"
201
+ : "dialog";
167
202
  }
168
- return "fullscreen";
203
+ return "dialog";
169
204
  case "dropdown":
170
205
  case undefined:
171
206
  case null:
@@ -264,17 +299,111 @@ export default class AuroFloatingUI {
264
299
  lockScroll(lock = true) {
265
300
  const element = this.element;
266
301
 
267
- if (lock) {
268
- if (!element?.bib) {
269
- return;
302
+ if (!element?.bib) {
303
+ return;
304
+ }
305
+
306
+ const dialog = (
307
+ element.bib?.shadowRoot ||
308
+ element.bib ||
309
+ element
310
+ ).querySelector("dialog");
311
+ if (dialog) {
312
+ if (lock) {
313
+ dialog.setAttribute("aria-modal", "true");
314
+ } else {
315
+ dialog.removeAttribute("aria-modal");
270
316
  }
317
+ }
271
318
 
272
- document.body.style.overflow = "hidden"; // hide body's scrollbar
319
+ if (lock) {
320
+ if (!this._scrollLocked) {
321
+ this._scrollLocked = true;
322
+ this._savedScrollY = window.scrollY;
323
+ this._savedScrollStyles = {
324
+ rootScrollbarGutter: document.documentElement.style.scrollbarGutter,
325
+ rootOverflow: document.documentElement.style.overflow,
326
+ bodyOverflow: document.body.style.overflow,
327
+ bodyPosition: document.body.style.position,
328
+ bodyTop: document.body.style.top,
329
+ bodyWidth: document.body.style.width,
330
+ bibTransform: element?.bib?.style.transform,
331
+ };
332
+ document.documentElement.style.scrollbarGutter = "stable";
333
+ document.documentElement.style.overflow = "hidden";
334
+ document.body.style.overflow = "hidden";
335
+
336
+ // position:fixed is the only way to block VoiceOver three-finger swipe,
337
+ // which bypasses both overflow:hidden and touchmove preventDefault.
338
+ document.body.style.position = "fixed";
339
+ document.body.style.top = `-${this._savedScrollY}px`;
340
+ document.body.style.width = "100%";
341
+
342
+ // Move `bib` by the amount the viewport is shifted to stay aligned in fullscreen.
343
+ if (element?.bib && window?.visualViewport?.offsetTop) {
344
+ element.bib.style.transform = `translateY(${window?.visualViewport?.offsetTop}px)`;
345
+ }
273
346
 
274
- // Move `bib` by the amount the viewport is shifted to stay aligned in fullscreen.
275
- element.bib.style.transform = `translateY(${window?.visualViewport?.offsetTop}px)`;
347
+ this.lockTouchScroll(true);
348
+ }
276
349
  } else {
277
- document.body.style.overflow = "";
350
+ if (this._scrollLocked) {
351
+ document.documentElement.style.scrollbarGutter =
352
+ this._savedScrollStyles?.rootScrollbarGutter ?? "";
353
+ document.documentElement.style.overflow =
354
+ this._savedScrollStyles?.rootOverflow ?? "";
355
+ document.body.style.overflow =
356
+ this._savedScrollStyles?.bodyOverflow ?? "";
357
+ document.body.style.position =
358
+ this._savedScrollStyles?.bodyPosition ?? "";
359
+ document.body.style.top = this._savedScrollStyles?.bodyTop ?? "";
360
+ document.body.style.width = this._savedScrollStyles?.bodyWidth ?? "";
361
+ if (element?.bib) {
362
+ element.bib.style.transform =
363
+ this._savedScrollStyles?.bibTransform ?? "";
364
+ }
365
+ window.scrollTo(0, this._savedScrollY || 0);
366
+ this._savedScrollY = undefined;
367
+ this._savedScrollStyles = undefined;
368
+ this._scrollLocked = false;
369
+
370
+ this.lockTouchScroll(false);
371
+ }
372
+ }
373
+ }
374
+
375
+ /**
376
+ * Locks page-level touch scroll while bib is open.
377
+ * Walks composedPath() so scrollable children inside the dialog still scroll.
378
+ * @private
379
+ */
380
+ lockTouchScroll(lock = true) {
381
+ if (lock) {
382
+ if (this._boundTouchMoveHandler) {
383
+ return;
384
+ }
385
+ this._boundTouchMoveHandler = (e) => {
386
+ const path = e.composedPath();
387
+ const insideScrollable = path.some(
388
+ (el) =>
389
+ el !== document &&
390
+ el !== document.documentElement &&
391
+ el !== document.body &&
392
+ (el.scrollHeight > el.clientHeight ||
393
+ el.scrollWidth > el.clientWidth),
394
+ );
395
+ if (!insideScrollable) {
396
+ e.preventDefault();
397
+ }
398
+ };
399
+ document.addEventListener("touchmove", this._boundTouchMoveHandler, {
400
+ passive: false,
401
+ });
402
+ } else if (this._boundTouchMoveHandler) {
403
+ document.removeEventListener("touchmove", this._boundTouchMoveHandler, {
404
+ passive: false,
405
+ });
406
+ this._boundTouchMoveHandler = undefined;
278
407
  }
279
408
  }
280
409
 
@@ -293,7 +422,7 @@ export default class AuroFloatingUI {
293
422
  return;
294
423
  }
295
424
 
296
- if (value === "fullscreen") {
425
+ if (value === "fullscreen" || value === "dialog") {
297
426
  element.isBibFullscreen = true;
298
427
  // reset the prev position
299
428
  element.bib.setAttribute("isfullscreen", "");
@@ -321,7 +450,7 @@ export default class AuroFloatingUI {
321
450
  }
322
451
 
323
452
  if (element.isPopoverVisible) {
324
- this.lockScroll(true);
453
+ this.lockScroll(value === "fullscreen");
325
454
  }
326
455
  } else {
327
456
  element.bib.style.position = "";
@@ -487,6 +616,28 @@ export default class AuroFloatingUI {
487
616
  setTimeout(() => {
488
617
  window.addEventListener("click", this.clickHandler);
489
618
  }, 0);
619
+
620
+ // iOS Safari does not fire `click` on non-interactive elements, so
621
+ // tapping an inert backdrop never reaches the click handler above.
622
+ // Mirror the same outside-tap logic with a passive touchstart listener.
623
+ this.touchHandler = (evt) => {
624
+ const element = this.element;
625
+ if (!element?.bib) {
626
+ return;
627
+ }
628
+
629
+ // fullscreen (modal) dialog handles its own dismissal
630
+ if (element.bib.hasAttribute("isfullscreen")) {
631
+ return;
632
+ }
633
+
634
+ const path = evt.composedPath();
635
+ if (!path.includes(element.trigger) && !path.includes(element.bib)) {
636
+ this.hideBib("click");
637
+ }
638
+ };
639
+
640
+ window.addEventListener("touchstart", this.touchHandler, { passive: true });
490
641
  }
491
642
 
492
643
  cleanupHideHandlers() {
@@ -502,6 +653,11 @@ export default class AuroFloatingUI {
502
653
  this.clickHandler = null;
503
654
  }
504
655
 
656
+ if (this.touchHandler) {
657
+ window.removeEventListener("touchstart", this.touchHandler);
658
+ this.touchHandler = null;
659
+ }
660
+
505
661
  if (this.keyDownHandler) {
506
662
  document.removeEventListener("keydown", this.keyDownHandler);
507
663
  this.keyDownHandler = null;
@@ -567,6 +723,12 @@ export default class AuroFloatingUI {
567
723
  this.position();
568
724
  },
569
725
  );
726
+
727
+ const idx = AuroFloatingUI.openingQueue.indexOf(this);
728
+ if (idx > -1) {
729
+ AuroFloatingUI.openingQueue.splice(idx, 1);
730
+ }
731
+ AuroFloatingUI.openingQueue.push(this);
570
732
  }
571
733
  }
572
734
 
@@ -607,6 +769,10 @@ export default class AuroFloatingUI {
607
769
  // Clearing it when hideBib is blocked (e.g. noToggle + click) corrupts
608
770
  // the singleton state so other dropdowns can't detect this one is still open.
609
771
  document.expandedAuroFloater = null;
772
+ const idx = AuroFloatingUI.openingQueue.indexOf(this);
773
+ if (idx > -1) {
774
+ AuroFloatingUI.openingQueue.splice(idx, 1);
775
+ }
610
776
  }
611
777
 
612
778
  /**
@@ -129,4 +129,271 @@ describe("AuroFloatingUI", () => {
129
129
 
130
130
  expect(floatingUI.getPositioningStrategy()).to.equal("floating");
131
131
  });
132
+
133
+ it("restores pre-existing inline scroll styles after lock/unlock", () => {
134
+ document.documentElement.style.scrollbarGutter = "auto";
135
+ document.documentElement.style.overflow = "clip";
136
+ document.body.style.overflow = "scroll";
137
+ document.body.style.position = "sticky";
138
+ document.body.style.top = "12px";
139
+ document.body.style.width = "75%";
140
+ const scrollToStub = sinon.stub(window, "scrollTo");
141
+
142
+ floatingUI.lockScroll(true);
143
+
144
+ expect(document.documentElement.style.scrollbarGutter).to.equal("stable");
145
+ expect(document.documentElement.style.overflow).to.equal("hidden");
146
+ expect(document.body.style.overflow).to.equal("hidden");
147
+ expect(document.body.style.position).to.equal("fixed");
148
+ expect(document.body.style.width).to.equal("100%");
149
+
150
+ floatingUI.lockScroll(false);
151
+
152
+ expect(document.documentElement.style.scrollbarGutter).to.equal("auto");
153
+ expect(document.documentElement.style.overflow).to.equal("clip");
154
+ expect(document.body.style.overflow).to.equal("scroll");
155
+ expect(document.body.style.position).to.equal("sticky");
156
+ expect(document.body.style.top).to.equal("12px");
157
+ expect(document.body.style.width).to.equal("75%");
158
+ expect(scrollToStub.calledOnceWithExactly(0, 0)).to.be.true;
159
+ expect(floatingUI._boundTouchMoveHandler).to.equal(undefined);
160
+ });
161
+
162
+ it("prevents touch scroll when gesture is outside scrollable content", () => {
163
+ floatingUI.lockTouchScroll(true);
164
+
165
+ const preventDefault = sinon.spy();
166
+ floatingUI._boundTouchMoveHandler({
167
+ composedPath: () => [document.body],
168
+ preventDefault,
169
+ });
170
+
171
+ expect(preventDefault.calledOnce).to.be.true;
172
+ });
173
+
174
+ it("allows touch scroll when gesture is inside scrollable content", () => {
175
+ floatingUI.lockTouchScroll(true);
176
+
177
+ const scrollable = document.createElement("div");
178
+ Object.defineProperty(scrollable, "scrollHeight", {
179
+ configurable: true,
180
+ value: 200,
181
+ });
182
+ Object.defineProperty(scrollable, "clientHeight", {
183
+ configurable: true,
184
+ value: 100,
185
+ });
186
+
187
+ const preventDefault = sinon.spy();
188
+ floatingUI._boundTouchMoveHandler({
189
+ composedPath: () => [scrollable],
190
+ preventDefault,
191
+ });
192
+
193
+ expect(preventDefault.called).to.be.false;
194
+ });
195
+ it("lockScroll sets _scrollLocked flag when locking", () => {
196
+ floatingUI.lockScroll(true);
197
+ expect(floatingUI._scrollLocked).to.be.true;
198
+ floatingUI.lockScroll(false);
199
+ expect(floatingUI._scrollLocked).to.be.false;
200
+ });
201
+
202
+ it("lockScroll saves scroll position when locking", () => {
203
+ const originalScrollY = window.scrollY;
204
+ floatingUI.lockScroll(true);
205
+ expect(floatingUI._savedScrollY).to.equal(originalScrollY);
206
+ });
207
+
208
+ it("lockScroll restores scroll position when unlocking", () => {
209
+ floatingUI.lockScroll(true);
210
+ window.scrollTo(0, 500);
211
+ floatingUI.lockScroll(false);
212
+ // Note: scrollTo may not work in test environment, but the restoration logic is in place
213
+ expect(floatingUI._scrollLocked).to.be.false;
214
+ });
215
+
216
+ it("lockTouchScroll creates event handler when locking", () => {
217
+ expect(floatingUI._boundTouchMoveHandler).to.be.undefined;
218
+ floatingUI.lockTouchScroll(true);
219
+ expect(floatingUI._boundTouchMoveHandler).to.be.a("function");
220
+ });
221
+
222
+ it("lockTouchScroll removes event handler when unlocking", () => {
223
+ floatingUI.lockTouchScroll(true);
224
+ floatingUI.lockTouchScroll(false);
225
+ expect(floatingUI._boundTouchMoveHandler).to.be.undefined;
226
+ });
227
+
228
+ it("lockTouchScroll prevents default on non-scrollable elements", () => {
229
+ floatingUI.lockTouchScroll(true);
230
+ const nonScrollable = document.createElement("div");
231
+ const preventDefault = sinon.spy();
232
+ const touchEvent = {
233
+ composedPath: () => [nonScrollable],
234
+ preventDefault,
235
+ };
236
+ floatingUI._boundTouchMoveHandler(touchEvent);
237
+ expect(preventDefault.called).to.be.true;
238
+ });
239
+
240
+ it("lockTouchScroll allows touchmove on scrollable elements", () => {
241
+ floatingUI.lockTouchScroll(true);
242
+ const scrollable = document.createElement("div");
243
+ Object.defineProperty(scrollable, "scrollHeight", {
244
+ configurable: true,
245
+ value: 200,
246
+ });
247
+ Object.defineProperty(scrollable, "clientHeight", {
248
+ configurable: true,
249
+ value: 100,
250
+ });
251
+ const preventDefault = sinon.spy();
252
+ const touchEvent = {
253
+ composedPath: () => [scrollable],
254
+ preventDefault,
255
+ };
256
+ floatingUI._boundTouchMoveHandler(touchEvent);
257
+ expect(preventDefault.called).to.be.false;
258
+ });
259
+
260
+ it("configureBibStrategy sets isfullscreen attribute for fullscreen", () => {
261
+ host.bib = bib;
262
+ host.isPopoverVisible = false;
263
+ floatingUI.configureBibStrategy("fullscreen");
264
+ expect(bib.getAttribute("isfullscreen")).to.equal("");
265
+ expect(host.isBibFullscreen).to.be.true;
266
+ });
267
+
268
+ it("configureBibStrategy removes isfullscreen attribute for floating", () => {
269
+ host.bib = bib;
270
+ bib.setAttribute("isfullscreen", "");
271
+ host.isBibFullscreen = true;
272
+ floatingUI.configureBibStrategy("floating");
273
+ expect(bib.hasAttribute("isfullscreen")).to.be.false;
274
+ expect(host.isBibFullscreen).to.be.false;
275
+ });
276
+
277
+ it("getPositioningStrategy returns 'dialog' for dialog behavior without breakpoint", () => {
278
+ floatingUI.behavior = "dialog";
279
+ host.floaterConfig = {};
280
+ const strategy = floatingUI.getPositioningStrategy();
281
+ expect(strategy).to.equal("dialog");
282
+ });
283
+
284
+ it("getPositioningStrategy returns 'floating' for undefined behavior", () => {
285
+ floatingUI.behavior = undefined;
286
+ host.floaterConfig = {};
287
+ const strategy = floatingUI.getPositioningStrategy();
288
+ expect(strategy).to.equal("floating");
289
+ });
290
+
291
+ it("getPositioningStrategy returns 'floating' when element is missing", () => {
292
+ floatingUI.element = null;
293
+ const strategy = floatingUI.getPositioningStrategy();
294
+ expect(strategy).to.equal("floating");
295
+ });
296
+ });
297
+
298
+ describe("AuroFloatingUI.openingQueue and topOpeningFloatingUI", () => {
299
+ let floatingUI1;
300
+ let floatingUI2;
301
+ let floatingUI3;
302
+ let host1;
303
+ let host2;
304
+ let host3;
305
+ let bib1;
306
+ let bib2;
307
+ let bib3;
308
+
309
+ beforeEach(() => {
310
+ AuroFloatingUI.openingQueue = [];
311
+ document.expandedAuroFormkitDropdown = null;
312
+ document.expandedAuroFloater = null;
313
+
314
+ host1 = document.createElement("div");
315
+ bib1 = document.createElement("div");
316
+ host1.bib = bib1;
317
+ host1.isPopoverVisible = false;
318
+ document.body.append(host1, bib1);
319
+ floatingUI1 = new AuroFloatingUI(host1, "dropdown");
320
+
321
+ host2 = document.createElement("div");
322
+ bib2 = document.createElement("div");
323
+ host2.bib = bib2;
324
+ host2.isPopoverVisible = false;
325
+ document.body.append(host2, bib2);
326
+ floatingUI2 = new AuroFloatingUI(host2, "dropdown");
327
+
328
+ host3 = document.createElement("div");
329
+ bib3 = document.createElement("div");
330
+ host3.bib = bib3;
331
+ host3.isPopoverVisible = false;
332
+ document.body.append(host3, bib3);
333
+ floatingUI3 = new AuroFloatingUI(host3, "dropdown");
334
+ });
335
+
336
+ afterEach(() => {
337
+ AuroFloatingUI.openingQueue = [];
338
+ document.expandedAuroFormkitDropdown = null;
339
+ document.expandedAuroFloater = null;
340
+ host1?.remove();
341
+ host2?.remove();
342
+ host3?.remove();
343
+ bib1?.remove();
344
+ bib2?.remove();
345
+ bib3?.remove();
346
+ sinon.restore();
347
+ });
348
+
349
+ it("can add instances to openingQueue", () => {
350
+ AuroFloatingUI.openingQueue.push(floatingUI1);
351
+ expect(AuroFloatingUI.openingQueue).to.include(floatingUI1);
352
+ expect(AuroFloatingUI.openingQueue.length).to.equal(1);
353
+ });
354
+
355
+ it("maintains insertion order in openingQueue", () => {
356
+ AuroFloatingUI.openingQueue.push(floatingUI1);
357
+ AuroFloatingUI.openingQueue.push(floatingUI2);
358
+ expect(AuroFloatingUI.openingQueue[0]).to.equal(floatingUI1);
359
+ expect(AuroFloatingUI.openingQueue[1]).to.equal(floatingUI2);
360
+ });
361
+
362
+ it("can remove instances from openingQueue", () => {
363
+ AuroFloatingUI.openingQueue.push(floatingUI1);
364
+ AuroFloatingUI.openingQueue.push(floatingUI2);
365
+ const index = AuroFloatingUI.openingQueue.indexOf(floatingUI1);
366
+ AuroFloatingUI.openingQueue.splice(index, 1);
367
+ expect(AuroFloatingUI.openingQueue).to.not.include(floatingUI1);
368
+ expect(AuroFloatingUI.openingQueue).to.include(floatingUI2);
369
+ });
370
+
371
+ it("topOpeningFloatingUI returns global reference when visible", () => {
372
+ document.expandedAuroFloater = floatingUI1;
373
+ floatingUI1.element.isPopoverVisible = true;
374
+ const topUI = AuroFloatingUI.topOpeningFloatingUI;
375
+ expect(topUI).to.equal(floatingUI1);
376
+ });
377
+
378
+ it("topOpeningFloatingUI returns queue last entry when global ref is stale", () => {
379
+ document.expandedAuroFloater = floatingUI1;
380
+ floatingUI1.element.isPopoverVisible = false;
381
+ AuroFloatingUI.openingQueue.push(floatingUI2);
382
+ floatingUI2.element.isPopoverVisible = true;
383
+ const topUI = AuroFloatingUI.topOpeningFloatingUI;
384
+ expect(topUI).to.equal(floatingUI2);
385
+ });
386
+
387
+ it("topOpeningFloatingUI returns last entry from queue", () => {
388
+ AuroFloatingUI.openingQueue.push(floatingUI1);
389
+ AuroFloatingUI.openingQueue.push(floatingUI2);
390
+ AuroFloatingUI.openingQueue.push(floatingUI3);
391
+ const topUI = AuroFloatingUI.topOpeningFloatingUI;
392
+ expect(topUI).to.equal(floatingUI3);
393
+ });
394
+
395
+ it("topOpeningFloatingUI returns null when queue is empty", () => {
396
+ const topUI = AuroFloatingUI.topOpeningFloatingUI;
397
+ expect(topUI).to.be.null;
398
+ });
132
399
  });