@aurodesignsystem/auro-library 5.11.2 → 5.12.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/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Semantic Release Automated Changelog
|
|
2
2
|
|
|
3
|
+
# [5.12.0](https://github.com/AlaskaAirlines/auro-library/compare/v5.11.3...v5.12.0) (2026-04-01)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* add keyboard handling toggle to AuroFloatingUI ([100bbe9](https://github.com/AlaskaAirlines/auro-library/commit/100bbe940ff2f1589d8b738ce83631bd5c7b8568))
|
|
9
|
+
|
|
10
|
+
## [5.11.3](https://github.com/AlaskaAirlines/auro-library/compare/v5.11.2...v5.11.3) (2026-03-31)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* correct focus local check for shadow DOM ([e713cfc](https://github.com/AlaskaAirlines/auro-library/commit/e713cfc07f1be4809c97e05af97b89050478e988))
|
|
16
|
+
|
|
3
17
|
## [5.11.2](https://github.com/AlaskaAirlines/auro-library/compare/v5.11.1...v5.11.2) (2026-03-17)
|
|
4
18
|
|
|
5
19
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aurodesignsystem/auro-library",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.12.0",
|
|
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",
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { expect } from "@open-wc/testing";
|
|
2
|
+
import { html, render } from "lit";
|
|
3
|
+
import sinon from "sinon";
|
|
4
|
+
import AuroFloatingUI from "../../floatingUI.mjs";
|
|
5
|
+
|
|
6
|
+
async function fixture(template) {
|
|
7
|
+
const wrapper = document.createElement("div");
|
|
8
|
+
render(template, wrapper);
|
|
9
|
+
document.body.appendChild(wrapper);
|
|
10
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
11
|
+
return wrapper.firstElementChild;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Builds a minimal element stub that satisfies the properties AuroFloatingUI
|
|
16
|
+
* reads during configure() and keyboard event handling.
|
|
17
|
+
*/
|
|
18
|
+
function makeElement(trigger, bib) {
|
|
19
|
+
return {
|
|
20
|
+
trigger,
|
|
21
|
+
bib,
|
|
22
|
+
bibSizer: null,
|
|
23
|
+
triggerChevron: null,
|
|
24
|
+
shadowRoot: {
|
|
25
|
+
querySelector: () => null,
|
|
26
|
+
append: () => {},
|
|
27
|
+
},
|
|
28
|
+
behavior: "dropdown",
|
|
29
|
+
disabled: false,
|
|
30
|
+
isPopoverVisible: false,
|
|
31
|
+
showing: false,
|
|
32
|
+
noToggle: false,
|
|
33
|
+
modal: false,
|
|
34
|
+
disableEventShow: false,
|
|
35
|
+
hoverToggle: false,
|
|
36
|
+
focusShow: false,
|
|
37
|
+
floaterConfig: null,
|
|
38
|
+
cleanup: null,
|
|
39
|
+
contains: () => false,
|
|
40
|
+
dispatchEvent: () => {},
|
|
41
|
+
getAttribute: () => null,
|
|
42
|
+
setAttribute: () => {},
|
|
43
|
+
querySelectorAll: () => [],
|
|
44
|
+
style: {},
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
describe("AuroFloatingUI keyboard gate (enableKeyboardHandling)", () => {
|
|
49
|
+
let triggerEl;
|
|
50
|
+
let bibEl;
|
|
51
|
+
let elem;
|
|
52
|
+
let floatingUi;
|
|
53
|
+
|
|
54
|
+
beforeEach(async () => {
|
|
55
|
+
triggerEl = await fixture(html`<button id="trigger">Toggle</button>`);
|
|
56
|
+
bibEl = await fixture(html`<div id="bib"></div>`);
|
|
57
|
+
|
|
58
|
+
elem = makeElement(triggerEl, bibEl);
|
|
59
|
+
floatingUi = new AuroFloatingUI(elem, "dropdown");
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
afterEach(async () => {
|
|
63
|
+
// Let any setTimeout(0) handlers (e.g. click listener setup in setupHideHandlers)
|
|
64
|
+
// fire before cleanup so cleanupHideHandlers can remove them.
|
|
65
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
66
|
+
document.expandedAuroFloater = null;
|
|
67
|
+
document.expandedAuroFormkitDropdown = null;
|
|
68
|
+
floatingUi?.disconnect();
|
|
69
|
+
floatingUi = null;
|
|
70
|
+
triggerEl?.parentNode?.remove();
|
|
71
|
+
bibEl?.parentNode?.remove();
|
|
72
|
+
sinon.restore();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe("default behavior (enableKeyboardHandling = true)", () => {
|
|
76
|
+
beforeEach(() => {
|
|
77
|
+
floatingUi.configure(elem, "auro-dropdown", true);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it("Enter on the trigger calls handleClick()", () => {
|
|
81
|
+
const spy = sinon.spy(floatingUi, "handleClick");
|
|
82
|
+
triggerEl.dispatchEvent(
|
|
83
|
+
new KeyboardEvent("keydown", {
|
|
84
|
+
key: "Enter",
|
|
85
|
+
bubbles: true,
|
|
86
|
+
composed: true,
|
|
87
|
+
}),
|
|
88
|
+
);
|
|
89
|
+
expect(spy.calledOnce).to.be.true;
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("Space on the trigger calls handleClick()", () => {
|
|
93
|
+
const spy = sinon.spy(floatingUi, "handleClick");
|
|
94
|
+
triggerEl.dispatchEvent(
|
|
95
|
+
new KeyboardEvent("keydown", {
|
|
96
|
+
key: " ",
|
|
97
|
+
bubbles: true,
|
|
98
|
+
composed: true,
|
|
99
|
+
}),
|
|
100
|
+
);
|
|
101
|
+
expect(spy.calledOnce).to.be.true;
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
it("Escape dismisses when bib is visible", () => {
|
|
105
|
+
elem.isPopoverVisible = true;
|
|
106
|
+
floatingUi.showing = true;
|
|
107
|
+
document.expandedAuroFloater = floatingUi;
|
|
108
|
+
const spy = sinon.spy(floatingUi, "hideBib");
|
|
109
|
+
|
|
110
|
+
// setupHideHandlers attaches the document keydown listener
|
|
111
|
+
floatingUi.setupHideHandlers();
|
|
112
|
+
document.dispatchEvent(
|
|
113
|
+
new KeyboardEvent("keydown", { key: "Escape", bubbles: true }),
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
expect(spy.calledOnce).to.be.true;
|
|
117
|
+
document.expandedAuroFloater = null;
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
describe("keyboard disabled (enableKeyboardHandling = false)", () => {
|
|
122
|
+
beforeEach(() => {
|
|
123
|
+
floatingUi.configure(elem, "auro-dropdown", false);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it("Enter on the trigger does NOT call handleClick()", () => {
|
|
127
|
+
const spy = sinon.spy(floatingUi, "handleClick");
|
|
128
|
+
triggerEl.dispatchEvent(
|
|
129
|
+
new KeyboardEvent("keydown", {
|
|
130
|
+
key: "Enter",
|
|
131
|
+
bubbles: true,
|
|
132
|
+
composed: true,
|
|
133
|
+
}),
|
|
134
|
+
);
|
|
135
|
+
expect(spy.called).to.be.false;
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it("Space on the trigger does NOT call handleClick()", () => {
|
|
139
|
+
const spy = sinon.spy(floatingUi, "handleClick");
|
|
140
|
+
triggerEl.dispatchEvent(
|
|
141
|
+
new KeyboardEvent("keydown", {
|
|
142
|
+
key: " ",
|
|
143
|
+
bubbles: true,
|
|
144
|
+
composed: true,
|
|
145
|
+
}),
|
|
146
|
+
);
|
|
147
|
+
expect(spy.called).to.be.false;
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it("Escape does NOT dismiss the bib", () => {
|
|
151
|
+
elem.isPopoverVisible = true;
|
|
152
|
+
floatingUi.showing = true;
|
|
153
|
+
document.expandedAuroFloater = floatingUi;
|
|
154
|
+
const spy = sinon.spy(floatingUi, "hideBib");
|
|
155
|
+
|
|
156
|
+
// setupHideHandlers should skip attaching the document keydown listener
|
|
157
|
+
floatingUi.setupHideHandlers();
|
|
158
|
+
document.dispatchEvent(
|
|
159
|
+
new KeyboardEvent("keydown", { key: "Escape", bubbles: true }),
|
|
160
|
+
);
|
|
161
|
+
|
|
162
|
+
expect(spy.called).to.be.false;
|
|
163
|
+
document.expandedAuroFloater = null;
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it("click on the trigger still calls handleClick()", () => {
|
|
167
|
+
const spy = sinon.spy(floatingUi, "handleClick");
|
|
168
|
+
triggerEl.dispatchEvent(
|
|
169
|
+
new MouseEvent("click", { bubbles: true, composed: true }),
|
|
170
|
+
);
|
|
171
|
+
expect(spy.calledOnce).to.be.true;
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
describe("default argument", () => {
|
|
176
|
+
it("omitting enableKeyboardHandling defaults to true (Enter still works)", () => {
|
|
177
|
+
floatingUi.configure(elem, "auro-dropdown");
|
|
178
|
+
const spy = sinon.spy(floatingUi, "handleClick");
|
|
179
|
+
triggerEl.dispatchEvent(
|
|
180
|
+
new KeyboardEvent("keydown", {
|
|
181
|
+
key: "Enter",
|
|
182
|
+
bubbles: true,
|
|
183
|
+
composed: true,
|
|
184
|
+
}),
|
|
185
|
+
);
|
|
186
|
+
expect(spy.calledOnce).to.be.true;
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
});
|
|
@@ -71,6 +71,11 @@ export default class AuroFloatingUI {
|
|
|
71
71
|
this.clickHandler = null;
|
|
72
72
|
this.keyDownHandler = null;
|
|
73
73
|
|
|
74
|
+
/**
|
|
75
|
+
* @private
|
|
76
|
+
*/
|
|
77
|
+
this.enableKeyboardHandling = true;
|
|
78
|
+
|
|
74
79
|
/**
|
|
75
80
|
* @private
|
|
76
81
|
*/
|
|
@@ -331,11 +336,10 @@ export default class AuroFloatingUI {
|
|
|
331
336
|
return;
|
|
332
337
|
}
|
|
333
338
|
|
|
334
|
-
const { activeElement } = document;
|
|
335
339
|
// if focus is still inside of trigger or bib, do not close
|
|
336
340
|
if (
|
|
337
|
-
this.element.
|
|
338
|
-
this.element.
|
|
341
|
+
this.element.matches(":focus") ||
|
|
342
|
+
this.element.matches(":focus-within")
|
|
339
343
|
) {
|
|
340
344
|
return;
|
|
341
345
|
}
|
|
@@ -406,7 +410,9 @@ export default class AuroFloatingUI {
|
|
|
406
410
|
document.addEventListener("focusin", this.focusHandler);
|
|
407
411
|
}
|
|
408
412
|
|
|
409
|
-
|
|
413
|
+
if (this.enableKeyboardHandling) {
|
|
414
|
+
document.addEventListener("keydown", this.keyDownHandler);
|
|
415
|
+
}
|
|
410
416
|
|
|
411
417
|
// send this task to the end of queue to prevent conflicting
|
|
412
418
|
// it conflicts if showBib gets call from a button that's not this.element.trigger
|
|
@@ -663,8 +669,9 @@ export default class AuroFloatingUI {
|
|
|
663
669
|
this.element.bib.setAttribute("id", `${this.id}-floater-bib`);
|
|
664
670
|
}
|
|
665
671
|
|
|
666
|
-
configure(elem, eventPrefix) {
|
|
672
|
+
configure(elem, eventPrefix, enableKeyboardHandling = true) {
|
|
667
673
|
AuroFloatingUI.setupMousePressChecker();
|
|
674
|
+
this.enableKeyboardHandling = enableKeyboardHandling;
|
|
668
675
|
|
|
669
676
|
this.eventPrefix = eventPrefix;
|
|
670
677
|
if (this.element !== elem) {
|
|
@@ -697,7 +704,9 @@ export default class AuroFloatingUI {
|
|
|
697
704
|
|
|
698
705
|
this.handleEvent = this.handleEvent.bind(this);
|
|
699
706
|
if (this.element.trigger) {
|
|
700
|
-
|
|
707
|
+
if (this.enableKeyboardHandling) {
|
|
708
|
+
this.element.trigger.addEventListener("keydown", this.handleEvent);
|
|
709
|
+
}
|
|
701
710
|
this.element.trigger.addEventListener("click", this.handleEvent);
|
|
702
711
|
this.element.trigger.addEventListener("mouseenter", this.handleEvent);
|
|
703
712
|
this.element.trigger.addEventListener("mouseleave", this.handleEvent);
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { expect } from "@open-wc/testing";
|
|
2
|
+
import sinon from "sinon";
|
|
3
|
+
import AuroFloatingUI from "./floatingUI.mjs";
|
|
4
|
+
|
|
5
|
+
describe("AuroFloatingUI", () => {
|
|
6
|
+
let host;
|
|
7
|
+
let bib;
|
|
8
|
+
let floatingUI;
|
|
9
|
+
let hideBibSpy;
|
|
10
|
+
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
host = document.createElement("div");
|
|
13
|
+
bib = document.createElement("div");
|
|
14
|
+
host.bib = bib;
|
|
15
|
+
host.triggerChevron = document.createElement("span");
|
|
16
|
+
|
|
17
|
+
document.body.append(host, bib);
|
|
18
|
+
|
|
19
|
+
AuroFloatingUI.isMousePressed = false;
|
|
20
|
+
floatingUI = new AuroFloatingUI(host, "dropdown");
|
|
21
|
+
hideBibSpy = sinon.spy(floatingUI, "hideBib");
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
afterEach(() => {
|
|
25
|
+
sinon.restore();
|
|
26
|
+
AuroFloatingUI.isMousePressed = false;
|
|
27
|
+
host?.remove();
|
|
28
|
+
bib?.remove();
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it("does not hide when the host matches focus-within", () => {
|
|
32
|
+
const checkedSelectors = [];
|
|
33
|
+
|
|
34
|
+
sinon.stub(host, "matches").callsFake((selector) => {
|
|
35
|
+
checkedSelectors.push(selector);
|
|
36
|
+
|
|
37
|
+
if (selector === ":focus") {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (selector === ":focus-within") {
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return false;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
floatingUI.handleFocusLoss();
|
|
49
|
+
|
|
50
|
+
expect(checkedSelectors).to.deep.equal([":focus", ":focus-within"]);
|
|
51
|
+
expect(hideBibSpy.called).to.be.false;
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("does not hide when the host matches focus", () => {
|
|
55
|
+
const checkedSelectors = [];
|
|
56
|
+
|
|
57
|
+
sinon.stub(host, "matches").callsFake((selector) => {
|
|
58
|
+
checkedSelectors.push(selector);
|
|
59
|
+
|
|
60
|
+
if (selector === ":focus") {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return false;
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
floatingUI.handleFocusLoss();
|
|
68
|
+
|
|
69
|
+
expect(checkedSelectors).to.deep.equal([":focus"]);
|
|
70
|
+
expect(hideBibSpy.called).to.be.false;
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it("does not hide when the bib is fullscreen", () => {
|
|
74
|
+
const checkedSelectors = [];
|
|
75
|
+
|
|
76
|
+
bib.setAttribute("isfullscreen", "");
|
|
77
|
+
|
|
78
|
+
sinon.stub(host, "matches").callsFake((selector) => {
|
|
79
|
+
checkedSelectors.push(selector);
|
|
80
|
+
return false;
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
floatingUI.handleFocusLoss();
|
|
84
|
+
|
|
85
|
+
expect(checkedSelectors).to.deep.equal([":focus", ":focus-within"]);
|
|
86
|
+
expect(hideBibSpy.called).to.be.false;
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it("hides with a keydown event when the host no longer has focus", () => {
|
|
90
|
+
const checkedSelectors = [];
|
|
91
|
+
|
|
92
|
+
sinon.stub(host, "matches").callsFake((selector) => {
|
|
93
|
+
checkedSelectors.push(selector);
|
|
94
|
+
return false;
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
floatingUI.handleFocusLoss();
|
|
98
|
+
|
|
99
|
+
expect(checkedSelectors).to.deep.equal([":focus", ":focus-within"]);
|
|
100
|
+
expect(hideBibSpy.calledOnceWithExactly("keydown")).to.be.true;
|
|
101
|
+
});
|
|
102
|
+
});
|