@aurodesignsystem/auro-library 5.13.2 → 5.14.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/.tool-versions +1 -1
- package/CHANGELOG.md +14 -0
- package/README.md +32 -0
- package/package.json +5 -4
- package/scripts/build/deprecatedProseToFieldPlugin.mjs +66 -0
- package/scripts/build/deprecatedProseToFieldPlugin.spec.js +188 -0
- package/scripts/runtime/floatingUI.mjs +18 -0
- package/scripts/runtime/floatingUI.test.js +79 -0
package/.tool-versions
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
nodejs
|
|
1
|
+
nodejs 22.22.3
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Semantic Release Automated Changelog
|
|
2
2
|
|
|
3
|
+
# [5.14.0](https://github.com/AlaskaAirlines/auro-library/compare/v5.13.3...v5.14.0) (2026-08-30)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* add shared deprecatedProseToFieldPlugin CEM build utility AB[#1633054](https://github.com/AlaskaAirlines/auro-library/issues/1633054) ([0230385](https://github.com/AlaskaAirlines/auro-library/commit/0230385fb7870411e00b05b8dc18204ce70e4e2f))
|
|
9
|
+
|
|
10
|
+
## [5.13.3](https://github.com/AlaskaAirlines/auro-library/compare/v5.13.2...v5.13.3) (2026-08-07)
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* **floatingUI:** suppress Escape keydown for modal dialogs to prevent CloseWatcher force-close AB[#1613688](https://github.com/AlaskaAirlines/auro-library/issues/1613688) ([efd81c5](https://github.com/AlaskaAirlines/auro-library/commit/efd81c5fd96f4d02253e4ce76b4e7a2ddb8d25c7))
|
|
16
|
+
|
|
3
17
|
## [5.13.2](https://github.com/AlaskaAirlines/auro-library/compare/v5.13.1...v5.13.2) (2026-07-28)
|
|
4
18
|
|
|
5
19
|
|
package/README.md
CHANGED
|
@@ -233,3 +233,35 @@ To run the `generateWcaComponent.mjs` script, you need to provide the file paths
|
|
|
233
233
|
"build:api:prepare": "node ./node_modules/@aurodesignsystem/auro-library/scripts/config/syncAllTemplates.mjs 'src/auro-flight*.js'"
|
|
234
234
|
```
|
|
235
235
|
<!-- AURO-GENERATED-CONTENT:END -->
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
### Deprecation Prose to CEM Field Plugin
|
|
240
|
+
|
|
241
|
+
<!-- AURO-GENERATED-CONTENT:START (FILE:src=./../docs/partials/deprecatedProseToFieldPlugin.md) -->
|
|
242
|
+
<!-- The below content is automatically added from ./../docs/partials/deprecatedProseToFieldPlugin.md -->
|
|
243
|
+
The `deprecatedProseToFieldPlugin` is a shared [Custom Elements Manifest](https://github.com/open-wc/custom-elements-manifest) analyzer plugin. It promotes deprecation prose written on `@event` and `@slot` tags into the machine-readable `deprecated` field of the generated `custom-elements.json`, so editor tooling can strike deprecated events and slots through.
|
|
244
|
+
|
|
245
|
+
The CEM analyzer already emits a `deprecated` field for attributes and members declared with an explicit `@deprecated` JSDoc tag, but it does **not** do so for events and slots declared as inline class-level `@event` / `@slot` tags. This plugin fills that gap by scanning each declaration's `events` and `slots` and, where a description contains a deprecation marker (`(Deprecated) …`, `**DEPRECATED** …`, or a leading `deprecated` word):
|
|
246
|
+
|
|
247
|
+
- sets `deprecated` to the guidance text that follows the marker and a separator (`-`, `:`, `–`, or `—`) — for example, `"Use the \`input\` event instead."`, or
|
|
248
|
+
- sets `deprecated` to `true` when the prose offers no replacement guidance.
|
|
249
|
+
|
|
250
|
+
Attributes and members are intentionally left untouched — the analyzer already flags those correctly. Entries that already carry a `deprecated` value are never overwritten.
|
|
251
|
+
|
|
252
|
+
## Usage
|
|
253
|
+
|
|
254
|
+
Import the plugin into your component library's `custom-elements-manifest.config.mjs` and add it to the `plugins` array:
|
|
255
|
+
|
|
256
|
+
```js
|
|
257
|
+
import { deprecatedProseToFieldPlugin } from '@aurodesignsystem/auro-library/scripts/build/deprecatedProseToFieldPlugin.mjs';
|
|
258
|
+
|
|
259
|
+
export default {
|
|
260
|
+
globs: ['components/**/src/*.js'],
|
|
261
|
+
litelement: true,
|
|
262
|
+
plugins: [deprecatedProseToFieldPlugin()]
|
|
263
|
+
};
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
Regenerating your `custom-elements.json` will now mark deprecated events and slots with their guidance messages.
|
|
267
|
+
<!-- AURO-GENERATED-CONTENT:END -->
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aurodesignsystem/auro-library",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.14.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",
|
|
@@ -27,10 +27,10 @@
|
|
|
27
27
|
"@open-wc/testing": "^4.0.0",
|
|
28
28
|
"@semantic-release/changelog": "^6.0.3",
|
|
29
29
|
"@semantic-release/git": "^10.0.1",
|
|
30
|
-
"@semantic-release/npm": "^
|
|
30
|
+
"@semantic-release/npm": "^13.0.0",
|
|
31
31
|
"husky": "^9.1.7",
|
|
32
32
|
"npm-run-all": "^4.1.5",
|
|
33
|
-
"semantic-release": "^
|
|
33
|
+
"semantic-release": "^25.0.0",
|
|
34
34
|
"sinon": "^20.0.0",
|
|
35
35
|
"vitest": "^2.1.2"
|
|
36
36
|
},
|
|
@@ -70,7 +70,8 @@
|
|
|
70
70
|
]
|
|
71
71
|
},
|
|
72
72
|
"publishConfig": {
|
|
73
|
-
"access": "public"
|
|
73
|
+
"access": "public",
|
|
74
|
+
"provenance": true
|
|
74
75
|
},
|
|
75
76
|
"scripts": {
|
|
76
77
|
"prepare": "husky install",
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Promote deprecation prose on events and slots into the CEM `deprecated` field.
|
|
3
|
+
*
|
|
4
|
+
* The analyzer (v0.11.0) emits a `deprecated` field for attributes and members
|
|
5
|
+
* from an explicit `@deprecated` JSDoc tag, but it does NOT do so for events and
|
|
6
|
+
* slots declared as inline class-level `@event` / `@slot` tags — so an event or
|
|
7
|
+
* slot whose description reads as deprecated ("(Deprecated) …", "**DEPRECATED** …")
|
|
8
|
+
* carries no machine-readable flag, and editor tooling cannot strike it through.
|
|
9
|
+
*
|
|
10
|
+
* This post-processing plugin scans each declaration's `events` and `slots`, and
|
|
11
|
+
* where the description contains a deprecation marker, sets `deprecated` to the
|
|
12
|
+
* guidance text following the marker (e.g. "Use `x` instead.") or `true` when the
|
|
13
|
+
* prose offers no replacement guidance. Attributes and members are intentionally
|
|
14
|
+
* left untouched — the analyzer already flags those correctly.
|
|
15
|
+
*
|
|
16
|
+
* Shared build utility for the Auro Design System. Import it into a component
|
|
17
|
+
* library's `custom-elements-manifest.config.mjs`:
|
|
18
|
+
*
|
|
19
|
+
* ```js
|
|
20
|
+
* import { deprecatedProseToFieldPlugin } from '@aurodesignsystem/auro-library/scripts/build/deprecatedProseToFieldPlugin.mjs';
|
|
21
|
+
*
|
|
22
|
+
* export default {
|
|
23
|
+
* // …
|
|
24
|
+
* plugins: [deprecatedProseToFieldPlugin()]
|
|
25
|
+
* };
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* @returns {{ name: string, packageLinkPhase: (context: { customElementsManifest: object }) => void }}
|
|
29
|
+
* A Custom Elements Manifest analyzer plugin.
|
|
30
|
+
*/
|
|
31
|
+
export function deprecatedProseToFieldPlugin() {
|
|
32
|
+
const MARKER = /\*\*deprecated\*\*|\(deprecated\)|(?:^|\s)deprecated\b/i;
|
|
33
|
+
const GUIDANCE =
|
|
34
|
+
/(?:\*\*deprecated\*\*|\(deprecated\)|(?:^|\s)deprecated\b)\s*[-:–—]\s*(.+)/is;
|
|
35
|
+
|
|
36
|
+
const promote = (entries) => {
|
|
37
|
+
if (!Array.isArray(entries)) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
for (const entry of entries) {
|
|
41
|
+
if (
|
|
42
|
+
entry.deprecated !== undefined ||
|
|
43
|
+
typeof entry.description !== "string"
|
|
44
|
+
) {
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (!MARKER.test(entry.description)) {
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
const guidance = entry.description.match(GUIDANCE);
|
|
51
|
+
entry.deprecated = guidance && guidance[1] ? guidance[1].trim() : true;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
name: "deprecated-prose-to-field",
|
|
57
|
+
packageLinkPhase({ customElementsManifest }) {
|
|
58
|
+
for (const mod of customElementsManifest.modules ?? []) {
|
|
59
|
+
for (const declaration of mod.declarations ?? []) {
|
|
60
|
+
promote(declaration.events);
|
|
61
|
+
promote(declaration.slots);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { deprecatedProseToFieldPlugin } from "./deprecatedProseToFieldPlugin.mjs";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Wrap one or more `entries` (events or slots) in a minimal Custom Elements
|
|
6
|
+
* Manifest shape, run the plugin's `packageLinkPhase` over it, and return the
|
|
7
|
+
* mutated entries so assertions can read their `deprecated` field.
|
|
8
|
+
*/
|
|
9
|
+
const run = (kind, entries) => {
|
|
10
|
+
const manifest = {
|
|
11
|
+
modules: [
|
|
12
|
+
{
|
|
13
|
+
declarations: [{ [kind]: entries }],
|
|
14
|
+
},
|
|
15
|
+
],
|
|
16
|
+
};
|
|
17
|
+
deprecatedProseToFieldPlugin().packageLinkPhase({
|
|
18
|
+
customElementsManifest: manifest,
|
|
19
|
+
});
|
|
20
|
+
return manifest.modules[0].declarations[0][kind];
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
describe("deprecatedProseToFieldPlugin", () => {
|
|
24
|
+
it("has the expected plugin name", () => {
|
|
25
|
+
expect(deprecatedProseToFieldPlugin().name).toBe(
|
|
26
|
+
"deprecated-prose-to-field",
|
|
27
|
+
);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe("marker + guidance -> string message", () => {
|
|
31
|
+
it('extracts guidance following a "(deprecated) -" marker', () => {
|
|
32
|
+
const [entry] = run("events", [
|
|
33
|
+
{
|
|
34
|
+
name: "change",
|
|
35
|
+
description: "(Deprecated) - Use the `input` event instead.",
|
|
36
|
+
},
|
|
37
|
+
]);
|
|
38
|
+
expect(entry.deprecated).toBe("Use the `input` event instead.");
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("extracts guidance for each supported separator (- : – —)", () => {
|
|
42
|
+
const separators = ["-", ":", "–", "—"];
|
|
43
|
+
separators.forEach((sep) => {
|
|
44
|
+
const [entry] = run("events", [
|
|
45
|
+
{
|
|
46
|
+
name: "change",
|
|
47
|
+
description: `**DEPRECATED** ${sep} Use \`input\` instead.`,
|
|
48
|
+
},
|
|
49
|
+
]);
|
|
50
|
+
expect(entry.deprecated).toBe("Use `input` instead.");
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("trims surrounding whitespace from the guidance text", () => {
|
|
55
|
+
const [entry] = run("slots", [
|
|
56
|
+
{
|
|
57
|
+
name: "legacy",
|
|
58
|
+
description: "deprecated: Use the `default` slot instead. ",
|
|
59
|
+
},
|
|
60
|
+
]);
|
|
61
|
+
expect(entry.deprecated).toBe("Use the `default` slot instead.");
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
describe("marker with no guidance -> boolean true", () => {
|
|
66
|
+
it('flags a "**DEPRECATED**" prose with no replacement guidance', () => {
|
|
67
|
+
const [entry] = run("slots", [
|
|
68
|
+
{ name: "legacy", description: "**DEPRECATED** legacy slot" },
|
|
69
|
+
]);
|
|
70
|
+
expect(entry.deprecated).toBe(true);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('flags a bare "(deprecated)" marker as true', () => {
|
|
74
|
+
const [entry] = run("events", [
|
|
75
|
+
{ name: "change", description: "(deprecated)" },
|
|
76
|
+
]);
|
|
77
|
+
expect(entry.deprecated).toBe(true);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe("non-deprecated entries are untouched", () => {
|
|
82
|
+
it("does not add a deprecated field when no marker is present", () => {
|
|
83
|
+
const [entry] = run("events", [
|
|
84
|
+
{ name: "input", description: "Fires when the value changes." },
|
|
85
|
+
]);
|
|
86
|
+
expect(entry).not.toHaveProperty("deprecated");
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
it('does not match unrelated words containing "deprecated" as a substring', () => {
|
|
90
|
+
const [entry] = run("events", [
|
|
91
|
+
{ name: "input", description: "This is undeprecated behavior." },
|
|
92
|
+
]);
|
|
93
|
+
expect(entry).not.toHaveProperty("deprecated");
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
describe("pre-existing deprecated values are preserved", () => {
|
|
98
|
+
it("does not overwrite an already-set string message", () => {
|
|
99
|
+
const [entry] = run("events", [
|
|
100
|
+
{
|
|
101
|
+
name: "change",
|
|
102
|
+
description: "(Deprecated) - Use `input`.",
|
|
103
|
+
deprecated: "existing guidance",
|
|
104
|
+
},
|
|
105
|
+
]);
|
|
106
|
+
expect(entry.deprecated).toBe("existing guidance");
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
it("does not overwrite an already-set boolean false", () => {
|
|
110
|
+
const [entry] = run("events", [
|
|
111
|
+
{
|
|
112
|
+
name: "change",
|
|
113
|
+
description: "(Deprecated) - Use `input`.",
|
|
114
|
+
deprecated: false,
|
|
115
|
+
},
|
|
116
|
+
]);
|
|
117
|
+
expect(entry.deprecated).toBe(false);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
describe("malformed / missing descriptions are skipped safely", () => {
|
|
122
|
+
it("skips entries whose description is not a string", () => {
|
|
123
|
+
const [noDesc, objDesc] = run("slots", [
|
|
124
|
+
{ name: "a" },
|
|
125
|
+
{ name: "b", description: { text: "deprecated" } },
|
|
126
|
+
]);
|
|
127
|
+
expect(noDesc).not.toHaveProperty("deprecated");
|
|
128
|
+
expect(objDesc).not.toHaveProperty("deprecated");
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
describe("applies to both events and slots, leaves other fields alone", () => {
|
|
133
|
+
it("promotes both events and slots on the same declaration", () => {
|
|
134
|
+
const manifest = {
|
|
135
|
+
modules: [
|
|
136
|
+
{
|
|
137
|
+
declarations: [
|
|
138
|
+
{
|
|
139
|
+
events: [
|
|
140
|
+
{ name: "change", description: "deprecated - Use `input`." },
|
|
141
|
+
],
|
|
142
|
+
slots: [
|
|
143
|
+
{ name: "legacy", description: "(Deprecated) legacy slot" },
|
|
144
|
+
],
|
|
145
|
+
attributes: [
|
|
146
|
+
{
|
|
147
|
+
name: "value",
|
|
148
|
+
description: "deprecated - do not touch me",
|
|
149
|
+
},
|
|
150
|
+
],
|
|
151
|
+
members: [
|
|
152
|
+
{ name: "reset", description: "(deprecated) leave me alone" },
|
|
153
|
+
],
|
|
154
|
+
},
|
|
155
|
+
],
|
|
156
|
+
},
|
|
157
|
+
],
|
|
158
|
+
};
|
|
159
|
+
deprecatedProseToFieldPlugin().packageLinkPhase({
|
|
160
|
+
customElementsManifest: manifest,
|
|
161
|
+
});
|
|
162
|
+
const [decl] = manifest.modules[0].declarations;
|
|
163
|
+
expect(decl.events[0].deprecated).toBe("Use `input`.");
|
|
164
|
+
expect(decl.slots[0].deprecated).toBe(true);
|
|
165
|
+
expect(decl.attributes[0]).not.toHaveProperty("deprecated");
|
|
166
|
+
expect(decl.members[0]).not.toHaveProperty("deprecated");
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
describe("does not throw on empty or absent manifest structures", () => {
|
|
171
|
+
it("handles a manifest with no modules", () => {
|
|
172
|
+
expect(() =>
|
|
173
|
+
deprecatedProseToFieldPlugin().packageLinkPhase({
|
|
174
|
+
customElementsManifest: {},
|
|
175
|
+
}),
|
|
176
|
+
).not.toThrow();
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it("handles declarations without events or slots arrays", () => {
|
|
180
|
+
const manifest = { modules: [{ declarations: [{}] }] };
|
|
181
|
+
expect(() =>
|
|
182
|
+
deprecatedProseToFieldPlugin().packageLinkPhase({
|
|
183
|
+
customElementsManifest: manifest,
|
|
184
|
+
}),
|
|
185
|
+
).not.toThrow();
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
});
|
|
@@ -778,6 +778,24 @@ export default class AuroFloatingUI {
|
|
|
778
778
|
if (!this.showing) {
|
|
779
779
|
if (!element.modal) {
|
|
780
780
|
this.setupHideHandlers();
|
|
781
|
+
} else {
|
|
782
|
+
if (this.keyDownHandler) {
|
|
783
|
+
document.removeEventListener("keydown", this.keyDownHandler);
|
|
784
|
+
}
|
|
785
|
+
this.keyDownHandler = (evt) => {
|
|
786
|
+
if (evt.key === "Escape" && element.isPopoverVisible) {
|
|
787
|
+
// Intercept at keydown so CloseWatcher never sees the keystroke.
|
|
788
|
+
// Canceling `cancel` alone is insufficient — a second Esc with no
|
|
789
|
+
// intervening user activation triggers the anti-trap and fires `close`
|
|
790
|
+
// directly, bypassing any `cancel` preventDefault (AB#1613688).
|
|
791
|
+
// stopImmediatePropagation is intentional: it prevents other document
|
|
792
|
+
// keydown handlers (e.g. consumer code, auro-dialog) from also acting
|
|
793
|
+
// on Escape while the modal owns the key.
|
|
794
|
+
evt.preventDefault();
|
|
795
|
+
evt.stopImmediatePropagation();
|
|
796
|
+
}
|
|
797
|
+
};
|
|
798
|
+
document.addEventListener("keydown", this.keyDownHandler);
|
|
781
799
|
}
|
|
782
800
|
this.showing = true;
|
|
783
801
|
element.isPopoverVisible = true;
|
|
@@ -295,6 +295,85 @@ describe("AuroFloatingUI", () => {
|
|
|
295
295
|
});
|
|
296
296
|
});
|
|
297
297
|
|
|
298
|
+
describe("AuroFloatingUI modal Escape suppression (AB#1613688)", () => {
|
|
299
|
+
let host;
|
|
300
|
+
let bib;
|
|
301
|
+
let floatingUI;
|
|
302
|
+
let hideBibSpy;
|
|
303
|
+
|
|
304
|
+
beforeEach(() => {
|
|
305
|
+
host = document.createElement("div");
|
|
306
|
+
bib = document.createElement("div");
|
|
307
|
+
host.bib = bib;
|
|
308
|
+
host.modal = true;
|
|
309
|
+
host.isPopoverVisible = false;
|
|
310
|
+
host.triggerChevron = document.createElement("span");
|
|
311
|
+
document.body.append(host, bib);
|
|
312
|
+
|
|
313
|
+
AuroFloatingUI.openingQueue = [];
|
|
314
|
+
document.expandedAuroFloater = null;
|
|
315
|
+
|
|
316
|
+
floatingUI = new AuroFloatingUI(host, "dialog");
|
|
317
|
+
hideBibSpy = sinon.spy(floatingUI, "hideBib");
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
afterEach(() => {
|
|
321
|
+
floatingUI.cleanupHideHandlers();
|
|
322
|
+
sinon.restore();
|
|
323
|
+
AuroFloatingUI.isMousePressed = false;
|
|
324
|
+
AuroFloatingUI.openingQueue = [];
|
|
325
|
+
document.expandedAuroFloater = null;
|
|
326
|
+
host?.remove();
|
|
327
|
+
bib?.remove();
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
it("registers a keydown handler and skips setupHideHandlers when modal=true", () => {
|
|
331
|
+
const setupHideHandlersSpy = sinon.spy(floatingUI, "setupHideHandlers");
|
|
332
|
+
|
|
333
|
+
floatingUI.showBib();
|
|
334
|
+
|
|
335
|
+
expect(setupHideHandlersSpy.called).to.be.false;
|
|
336
|
+
expect(floatingUI.keyDownHandler).to.be.a("function");
|
|
337
|
+
expect(floatingUI.showing).to.be.true;
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
it("does not call hideBib when Escape is pressed while modal dialog is open", () => {
|
|
341
|
+
floatingUI.showBib();
|
|
342
|
+
|
|
343
|
+
document.dispatchEvent(
|
|
344
|
+
new KeyboardEvent("keydown", {
|
|
345
|
+
key: "Escape",
|
|
346
|
+
bubbles: true,
|
|
347
|
+
cancelable: true,
|
|
348
|
+
}),
|
|
349
|
+
);
|
|
350
|
+
|
|
351
|
+
expect(hideBibSpy.called).to.be.false;
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
it("calls preventDefault on Escape keydown while modal dialog is open", () => {
|
|
355
|
+
floatingUI.showBib();
|
|
356
|
+
|
|
357
|
+
const escEvent = new KeyboardEvent("keydown", {
|
|
358
|
+
key: "Escape",
|
|
359
|
+
bubbles: true,
|
|
360
|
+
cancelable: true,
|
|
361
|
+
});
|
|
362
|
+
const preventDefaultSpy = sinon.spy(escEvent, "preventDefault");
|
|
363
|
+
document.dispatchEvent(escEvent);
|
|
364
|
+
|
|
365
|
+
expect(preventDefaultSpy.calledOnce).to.be.true;
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
it("removes the modal Escape keydown handler via cleanupHideHandlers", () => {
|
|
369
|
+
floatingUI.showBib();
|
|
370
|
+
expect(floatingUI.keyDownHandler).to.be.a("function");
|
|
371
|
+
|
|
372
|
+
floatingUI.cleanupHideHandlers();
|
|
373
|
+
expect(floatingUI.keyDownHandler).to.be.null;
|
|
374
|
+
});
|
|
375
|
+
});
|
|
376
|
+
|
|
298
377
|
describe("AuroFloatingUI.openingQueue and topOpeningFloatingUI", () => {
|
|
299
378
|
let floatingUI1;
|
|
300
379
|
let floatingUI2;
|