aria-ease 6.4.10 → 6.5.1
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/bin/{chunk-GFKAJHCS.js → chunk-AUJAN4RK.js} +34 -18
- package/bin/cli.cjs +67 -35
- package/bin/cli.js +1 -1
- package/{dist/contractTestRunnerPlaywright-7BCEDPZF.js → bin/contractTestRunnerPlaywright-7F756CFB.js} +26 -12
- package/bin/{test-JGKWOL6J.js → test-C3CMRHSI.js} +7 -5
- package/dist/{chunk-GFKAJHCS.js → chunk-AUJAN4RK.js} +34 -18
- package/{bin/contractTestRunnerPlaywright-7BCEDPZF.js → dist/contractTestRunnerPlaywright-7F756CFB.js} +26 -12
- package/dist/index.cjs +148 -106
- package/dist/index.js +89 -77
- package/dist/src/block/index.cjs +1 -6
- package/dist/src/block/index.js +72 -1
- package/dist/src/menu/index.cjs +79 -147
- package/dist/src/menu/index.js +79 -23
- package/dist/src/utils/test/{contracts/AccordionContract.json → aria-contracts/accordion/accordion.contract.json} +21 -8
- package/dist/src/utils/test/{contracts/ComboboxContract.json → aria-contracts/combobox/combobox.listbox.contract.json} +18 -18
- package/dist/src/utils/test/{contracts/MenuContract.json → aria-contracts/menu/menu.contract.json} +43 -2
- package/dist/src/utils/test/{contracts/TabsContract.json → aria-contracts/tabs/tabs.contract.json} +21 -8
- package/dist/src/utils/test/{chunk-GFKAJHCS.js → chunk-AUJAN4RK.js} +33 -17
- package/dist/src/utils/test/{contractTestRunnerPlaywright-O7FF3X67.js → contractTestRunnerPlaywright-HL73FADJ.js} +25 -11
- package/dist/src/utils/test/index.cjs +65 -33
- package/dist/src/utils/test/index.js +6 -4
- package/package.json +2 -2
- package/dist/src/chunk-ZJXZZDUR.js +0 -127
package/dist/index.cjs
CHANGED
|
@@ -37,29 +37,29 @@ var init_contract = __esm({
|
|
|
37
37
|
"src/utils/test/contract/contract.json"() {
|
|
38
38
|
contract_default = {
|
|
39
39
|
menu: {
|
|
40
|
-
path: "./contracts/
|
|
40
|
+
path: "./aria-contracts/menu/menu.contract.json",
|
|
41
41
|
component: "menu"
|
|
42
42
|
},
|
|
43
|
-
combobox: {
|
|
44
|
-
path: "./contracts/
|
|
45
|
-
component: "combobox"
|
|
43
|
+
"combobox.listbox": {
|
|
44
|
+
path: "./aria-contracts/combobox/combobox.listbox.contract.json",
|
|
45
|
+
component: "combobox.listbox"
|
|
46
46
|
},
|
|
47
47
|
accordion: {
|
|
48
|
-
path: "./contracts/
|
|
48
|
+
path: "./aria-contracts/accordion/accordion.contract.json",
|
|
49
49
|
component: "accordion"
|
|
50
50
|
},
|
|
51
51
|
tabs: {
|
|
52
|
-
path: "./contracts/
|
|
52
|
+
path: "./aria-contracts/tabs/tabs.contract.json",
|
|
53
53
|
component: "tabs"
|
|
54
54
|
}
|
|
55
55
|
};
|
|
56
56
|
}
|
|
57
57
|
});
|
|
58
58
|
|
|
59
|
-
// src/utils/test/
|
|
59
|
+
// src/utils/test/src/ContractReporter.ts
|
|
60
60
|
var ContractReporter;
|
|
61
61
|
var init_ContractReporter = __esm({
|
|
62
|
-
"src/utils/test/
|
|
62
|
+
"src/utils/test/src/ContractReporter.ts"() {
|
|
63
63
|
"use strict";
|
|
64
64
|
ContractReporter = class {
|
|
65
65
|
startTime = 0;
|
|
@@ -72,6 +72,8 @@ var init_ContractReporter = __esm({
|
|
|
72
72
|
optionalSuggestions = 0;
|
|
73
73
|
isPlaywright = false;
|
|
74
74
|
apgUrl = "https://www.w3.org/WAI/ARIA/apg/";
|
|
75
|
+
hasPrintedStaticSection = false;
|
|
76
|
+
hasPrintedDynamicSection = false;
|
|
75
77
|
constructor(isPlaywright = false) {
|
|
76
78
|
this.isPlaywright = isPlaywright;
|
|
77
79
|
}
|
|
@@ -82,30 +84,32 @@ var init_ContractReporter = __esm({
|
|
|
82
84
|
this.startTime = Date.now();
|
|
83
85
|
this.componentName = componentName;
|
|
84
86
|
this.totalTests = totalTests;
|
|
87
|
+
this.hasPrintedStaticSection = false;
|
|
88
|
+
this.hasPrintedDynamicSection = false;
|
|
85
89
|
if (apgUrl) {
|
|
86
90
|
this.apgUrl = apgUrl;
|
|
87
91
|
}
|
|
88
92
|
const mode = this.isPlaywright ? "Playwright (Real Browser)" : "jsdom (Fast)";
|
|
89
93
|
this.log(`
|
|
90
94
|
${"\u2550".repeat(60)}`);
|
|
91
|
-
this.log(`\u{1F50D} Testing ${componentName} Component - ${mode}`);
|
|
95
|
+
this.log(`\u{1F50D} Testing ${componentName.charAt(0).toUpperCase() + componentName.slice(1)} Component - ${mode}`);
|
|
92
96
|
this.log(`${"\u2550".repeat(60)}
|
|
93
97
|
`);
|
|
94
98
|
}
|
|
95
99
|
reportStatic(passes, failures) {
|
|
96
100
|
this.staticPasses = passes;
|
|
97
101
|
this.staticFailures = failures;
|
|
98
|
-
const icon = failures === 0 ? "\u2705" : "\u274C";
|
|
99
|
-
const status = failures === 0 ? "PASS" : "FAIL";
|
|
100
|
-
this.log("");
|
|
101
|
-
this.log(`${icon} Static ARIA Tests: ${status}`);
|
|
102
|
-
this.log(` ${passes}/${passes + failures} required attributes present
|
|
103
|
-
`);
|
|
104
102
|
}
|
|
105
103
|
/**
|
|
106
104
|
* Report individual static test pass
|
|
107
105
|
*/
|
|
108
106
|
reportStaticTest(description, passed, failureMessage) {
|
|
107
|
+
if (!this.hasPrintedStaticSection) {
|
|
108
|
+
this.log(`${"\u2500".repeat(60)}`);
|
|
109
|
+
this.log(`\u{1F9EA} Static Assertions`);
|
|
110
|
+
this.log(`${"\u2500".repeat(60)}`);
|
|
111
|
+
this.hasPrintedStaticSection = true;
|
|
112
|
+
}
|
|
109
113
|
const icon = passed ? "\u2713" : "\u2717";
|
|
110
114
|
this.log(` ${icon} ${description}`);
|
|
111
115
|
if (!passed && failureMessage) {
|
|
@@ -116,6 +120,13 @@ ${"\u2550".repeat(60)}`);
|
|
|
116
120
|
* Report individual dynamic test result
|
|
117
121
|
*/
|
|
118
122
|
reportTest(test, status, failureMessage) {
|
|
123
|
+
if (!this.hasPrintedDynamicSection) {
|
|
124
|
+
this.log("");
|
|
125
|
+
this.log(`${"\u2500".repeat(60)}`);
|
|
126
|
+
this.log(`\u2328\uFE0F Dynamic Interaction Tests`);
|
|
127
|
+
this.log(`${"\u2500".repeat(60)}`);
|
|
128
|
+
this.hasPrintedDynamicSection = true;
|
|
129
|
+
}
|
|
119
130
|
const result = {
|
|
120
131
|
description: test.description,
|
|
121
132
|
status,
|
|
@@ -202,7 +213,7 @@ ${"\u2500".repeat(60)}`);
|
|
|
202
213
|
});
|
|
203
214
|
this.log(`
|
|
204
215
|
\u{1F4A1} Run with Playwright for full validation:`);
|
|
205
|
-
this.log(` testUiComponent('${this.componentName}',
|
|
216
|
+
this.log(` testUiComponent('${this.componentName}', null, 'http://localhost:5173/test-harness?component=component_name')
|
|
206
217
|
`);
|
|
207
218
|
}
|
|
208
219
|
/**
|
|
@@ -226,9 +237,14 @@ ${"\u2500".repeat(60)}`);
|
|
|
226
237
|
${"\u2550".repeat(60)}`);
|
|
227
238
|
this.log(`\u{1F4CA} Summary
|
|
228
239
|
`);
|
|
240
|
+
const staticIcon = this.staticFailures === 0 ? "\u2705" : "\u274C";
|
|
241
|
+
const staticStatus = this.staticFailures === 0 ? "PASS" : "FAIL";
|
|
242
|
+
this.log(`${staticIcon} Static ARIA Tests: ${staticStatus}`);
|
|
243
|
+
this.log(` ${this.staticPasses}/${this.staticPasses + this.staticFailures} required attributes present`);
|
|
244
|
+
this.log("");
|
|
229
245
|
if (totalFailures === 0 && this.skipped === 0 && this.optionalSuggestions === 0) {
|
|
230
246
|
this.log(`\u2705 All ${totalRun} tests passed!`);
|
|
231
|
-
this.log(` ${this.componentName} component meets WAI-ARIA expectations for Roles, States, Properties, and Keyboard Interactions \u2713`);
|
|
247
|
+
this.log(` ${this.componentName.charAt(0).toUpperCase()}${this.componentName.slice(1)} component meets WAI-ARIA expectations for Roles, States, Properties, and Keyboard Interactions \u2713`);
|
|
232
248
|
} else if (totalFailures === 0) {
|
|
233
249
|
this.log(`\u2705 ${totalPasses}/${totalRun} required tests passed`);
|
|
234
250
|
if (this.skipped > 0) {
|
|
@@ -237,7 +253,7 @@ ${"\u2550".repeat(60)}`);
|
|
|
237
253
|
if (this.optionalSuggestions > 0) {
|
|
238
254
|
this.log(`\u{1F4A1} ${this.optionalSuggestions} optional enhancement${this.optionalSuggestions > 1 ? "s" : ""} suggested`);
|
|
239
255
|
}
|
|
240
|
-
this.log(` ${this.componentName} component meets WAI-ARIA expectations for Roles, States, Properties, and Keyboard Interactions \u2713`);
|
|
256
|
+
this.log(` ${this.componentName.charAt(0).toUpperCase()}${this.componentName.slice(1)} component meets WAI-ARIA expectations for Roles, States, Properties, and Keyboard Interactions \u2713`);
|
|
241
257
|
} else {
|
|
242
258
|
this.log(`\u274C ${totalFailures} test${totalFailures > 1 ? "s" : ""} failed`);
|
|
243
259
|
this.log(`\u2705 ${totalPasses} test${totalPasses > 1 ? "s" : ""} passed`);
|
|
@@ -283,7 +299,7 @@ ${"\u2550".repeat(60)}`);
|
|
|
283
299
|
}
|
|
284
300
|
});
|
|
285
301
|
|
|
286
|
-
// src/utils/test/
|
|
302
|
+
// src/utils/test/src/playwrightTestHarness.ts
|
|
287
303
|
async function getOrCreateBrowser() {
|
|
288
304
|
if (!sharedBrowser) {
|
|
289
305
|
sharedBrowser = await import_playwright.chromium.launch({
|
|
@@ -325,7 +341,7 @@ async function closeSharedBrowser() {
|
|
|
325
341
|
}
|
|
326
342
|
var import_playwright, sharedBrowser, sharedContext;
|
|
327
343
|
var init_playwrightTestHarness = __esm({
|
|
328
|
-
"src/utils/test/
|
|
344
|
+
"src/utils/test/src/playwrightTestHarness.ts"() {
|
|
329
345
|
"use strict";
|
|
330
346
|
import_playwright = require("playwright");
|
|
331
347
|
sharedBrowser = null;
|
|
@@ -597,7 +613,7 @@ var init_ComponentDetector = __esm({
|
|
|
597
613
|
const contractData = (0, import_fs.readFileSync)(resolvedPath, "utf-8");
|
|
598
614
|
const componentContract = JSON.parse(contractData);
|
|
599
615
|
const selectors = componentContract.selectors;
|
|
600
|
-
if (componentName
|
|
616
|
+
if (componentName.includes("combobox")) {
|
|
601
617
|
const mainSelector = selectors.input || selectors.container;
|
|
602
618
|
return new ComboboxComponentStrategy(mainSelector, selectors, actionTimeoutMs, assertionTimeoutMs);
|
|
603
619
|
}
|
|
@@ -1119,7 +1135,7 @@ var init_AssertionRunner = __esm({
|
|
|
1119
1135
|
}
|
|
1120
1136
|
});
|
|
1121
1137
|
|
|
1122
|
-
// src/utils/test/
|
|
1138
|
+
// src/utils/test/src/contractTestRunnerPlaywright.ts
|
|
1123
1139
|
var contractTestRunnerPlaywright_exports = {};
|
|
1124
1140
|
__export(contractTestRunnerPlaywright_exports, {
|
|
1125
1141
|
runContractTestsPlaywright: () => runContractTestsPlaywright
|
|
@@ -1163,7 +1179,7 @@ async function runContractTestsPlaywright(componentName, url) {
|
|
|
1163
1179
|
throw new Error(`CRITICAL: No selector found in contract for ${componentName}`);
|
|
1164
1180
|
}
|
|
1165
1181
|
try {
|
|
1166
|
-
await page.locator(mainSelector).first().waitFor({ state: "attached", timeout:
|
|
1182
|
+
await page.locator(mainSelector).first().waitFor({ state: "attached", timeout: 3e4 });
|
|
1167
1183
|
} catch (error) {
|
|
1168
1184
|
throw new Error(
|
|
1169
1185
|
`
|
|
@@ -1178,24 +1194,29 @@ This usually means:
|
|
|
1178
1194
|
reporter.start(componentName, totalTests, apgUrl);
|
|
1179
1195
|
if (componentName === "menu" && componentContract.selectors.trigger) {
|
|
1180
1196
|
await page.locator(componentContract.selectors.trigger).first().waitFor({
|
|
1181
|
-
state: "
|
|
1197
|
+
state: "attached",
|
|
1182
1198
|
timeout: 5e3
|
|
1183
1199
|
}).catch(() => {
|
|
1184
|
-
console.warn("Menu trigger not visible, continuing with tests...");
|
|
1185
1200
|
});
|
|
1186
1201
|
}
|
|
1202
|
+
const failuresBeforeStatic = failures.length;
|
|
1187
1203
|
const staticAssertionRunner = new AssertionRunner(page, componentContract.selectors, assertionTimeoutMs);
|
|
1188
1204
|
for (const test of componentContract.static[0]?.assertions || []) {
|
|
1189
1205
|
if (test.target === "relative") continue;
|
|
1206
|
+
const staticDescription = `${test.target}${test.attribute ? ` (${test.attribute})` : ""}`;
|
|
1190
1207
|
const targetSelector = componentContract.selectors[test.target];
|
|
1191
1208
|
if (!targetSelector) {
|
|
1192
|
-
|
|
1209
|
+
const failure = `Selector for target ${test.target} not found.`;
|
|
1210
|
+
failures.push(failure);
|
|
1211
|
+
reporter.reportStaticTest(staticDescription, false, failure);
|
|
1193
1212
|
continue;
|
|
1194
1213
|
}
|
|
1195
1214
|
const target = page.locator(targetSelector).first();
|
|
1196
1215
|
const exists = await target.count() > 0;
|
|
1197
1216
|
if (!exists) {
|
|
1198
|
-
|
|
1217
|
+
const failure = `Target ${test.target} not found.`;
|
|
1218
|
+
failures.push(failure);
|
|
1219
|
+
reporter.reportStaticTest(staticDescription, false, failure);
|
|
1199
1220
|
continue;
|
|
1200
1221
|
}
|
|
1201
1222
|
const isRedundantCheck = (selector, attrName, expectedVal) => {
|
|
@@ -1229,13 +1250,19 @@ This usually means:
|
|
|
1229
1250
|
}
|
|
1230
1251
|
}
|
|
1231
1252
|
if (!hasAny && !allRedundant) {
|
|
1232
|
-
|
|
1253
|
+
const failure = test.failureMessage + ` None of the attributes "${test.attribute}" are present.`;
|
|
1254
|
+
failures.push(failure);
|
|
1255
|
+
reporter.reportStaticTest(staticDescription, false, failure);
|
|
1233
1256
|
} else if (!allRedundant && hasAny) {
|
|
1234
1257
|
passes.push(`At least one of the attributes "${test.attribute}" exists on the element.`);
|
|
1258
|
+
reporter.reportStaticTest(staticDescription, true);
|
|
1259
|
+
} else {
|
|
1260
|
+
reporter.reportStaticTest(staticDescription, true);
|
|
1235
1261
|
}
|
|
1236
1262
|
} else {
|
|
1237
1263
|
if (isRedundantCheck(targetSelector, test.attribute, test.expectedValue)) {
|
|
1238
1264
|
passes.push(`${test.attribute}="${test.expectedValue}" on ${test.target} verified by selector (already present in: ${targetSelector}).`);
|
|
1265
|
+
reporter.reportStaticTest(staticDescription, true);
|
|
1239
1266
|
} else {
|
|
1240
1267
|
const result = await staticAssertionRunner.validateAttribute(
|
|
1241
1268
|
target,
|
|
@@ -1247,8 +1274,10 @@ This usually means:
|
|
|
1247
1274
|
);
|
|
1248
1275
|
if (result.success && result.passMessage) {
|
|
1249
1276
|
passes.push(result.passMessage);
|
|
1277
|
+
reporter.reportStaticTest(staticDescription, true);
|
|
1250
1278
|
} else if (!result.success && result.failMessage) {
|
|
1251
1279
|
failures.push(result.failMessage);
|
|
1280
|
+
reporter.reportStaticTest(staticDescription, false, result.failMessage);
|
|
1252
1281
|
}
|
|
1253
1282
|
}
|
|
1254
1283
|
}
|
|
@@ -1327,8 +1356,9 @@ This usually means:
|
|
|
1327
1356
|
reporter.reportTest(dynamicTest, testPassed ? "pass" : "fail", failureMessage);
|
|
1328
1357
|
}
|
|
1329
1358
|
}
|
|
1330
|
-
const
|
|
1331
|
-
const staticFailed =
|
|
1359
|
+
const staticTotal = componentContract.static[0].assertions.length;
|
|
1360
|
+
const staticFailed = failures.length - failuresBeforeStatic;
|
|
1361
|
+
const staticPassed = Math.max(0, staticTotal - staticFailed);
|
|
1332
1362
|
reporter.reportStatic(staticPassed, staticFailed);
|
|
1333
1363
|
reporter.summary(failures);
|
|
1334
1364
|
} catch (error) {
|
|
@@ -1358,7 +1388,7 @@ Make sure your dev server is running at ${url}`);
|
|
|
1358
1388
|
}
|
|
1359
1389
|
var import_fs2, import_meta3;
|
|
1360
1390
|
var init_contractTestRunnerPlaywright = __esm({
|
|
1361
|
-
"src/utils/test/
|
|
1391
|
+
"src/utils/test/src/contractTestRunnerPlaywright.ts"() {
|
|
1362
1392
|
"use strict";
|
|
1363
1393
|
import_fs2 = require("fs");
|
|
1364
1394
|
init_contract();
|
|
@@ -1704,38 +1734,11 @@ function moveFocus(elementItems, currentIndex, direction) {
|
|
|
1704
1734
|
function isClickableButNotSemantic(el) {
|
|
1705
1735
|
return el.getAttribute("data-custom-click") !== null && el.getAttribute("data-custom-click") !== void 0;
|
|
1706
1736
|
}
|
|
1707
|
-
function
|
|
1708
|
-
menuElement.style.display = "none";
|
|
1709
|
-
const menuTriggerButtonId = menuTriggerButton.getAttribute("id");
|
|
1710
|
-
if (!menuTriggerButtonId) {
|
|
1711
|
-
console.error("[aria-ease] Menu trigger button must have an id attribute to properly set aria attributes.");
|
|
1712
|
-
return;
|
|
1713
|
-
}
|
|
1714
|
-
menuTriggerButton.setAttribute("aria-expanded", "false");
|
|
1715
|
-
}
|
|
1716
|
-
function hasSubmenu(menuItem) {
|
|
1717
|
-
return menuItem.getAttribute("aria-haspopup") === "true" || menuItem.getAttribute("aria-haspopup") === "menu";
|
|
1718
|
-
}
|
|
1719
|
-
function getSubmenuId(menuItem) {
|
|
1720
|
-
return menuItem.getAttribute("aria-controls");
|
|
1721
|
-
}
|
|
1722
|
-
function handleKeyPress(event, elementItems, elementItemIndex, menuElementDiv, triggerButton, openSubmenu, closeSubmenu, onOpenChange) {
|
|
1737
|
+
function handleKeyPress(event, elementItems, elementItemIndex) {
|
|
1723
1738
|
const currentEl = elementItems.item(elementItemIndex);
|
|
1724
1739
|
switch (event.key) {
|
|
1725
1740
|
case "ArrowUp":
|
|
1726
1741
|
case "ArrowLeft": {
|
|
1727
|
-
if (event.key === "ArrowLeft" && menuElementDiv && closeSubmenu) {
|
|
1728
|
-
const labelledBy = menuElementDiv.getAttribute("aria-labelledby");
|
|
1729
|
-
if (labelledBy) {
|
|
1730
|
-
const parentTrigger = document.getElementById(labelledBy);
|
|
1731
|
-
if (parentTrigger && parentTrigger.getAttribute("role") === "menuitem") {
|
|
1732
|
-
event.preventDefault();
|
|
1733
|
-
closeSubmenu();
|
|
1734
|
-
parentTrigger.focus();
|
|
1735
|
-
return;
|
|
1736
|
-
}
|
|
1737
|
-
}
|
|
1738
|
-
}
|
|
1739
1742
|
if (!isTextInput(currentEl) && !isTextArea(currentEl)) {
|
|
1740
1743
|
event.preventDefault();
|
|
1741
1744
|
moveFocus(elementItems, elementItemIndex, -1);
|
|
@@ -1750,14 +1753,6 @@ function handleKeyPress(event, elementItems, elementItemIndex, menuElementDiv, t
|
|
|
1750
1753
|
}
|
|
1751
1754
|
case "ArrowDown":
|
|
1752
1755
|
case "ArrowRight": {
|
|
1753
|
-
if (event.key === "ArrowRight" && hasSubmenu(currentEl) && openSubmenu) {
|
|
1754
|
-
event.preventDefault();
|
|
1755
|
-
const submenuId = getSubmenuId(currentEl);
|
|
1756
|
-
if (submenuId) {
|
|
1757
|
-
openSubmenu(submenuId);
|
|
1758
|
-
return;
|
|
1759
|
-
}
|
|
1760
|
-
}
|
|
1761
1756
|
if (!isTextInput(currentEl) && !isTextArea(currentEl)) {
|
|
1762
1757
|
event.preventDefault();
|
|
1763
1758
|
moveFocus(elementItems, elementItemIndex, 1);
|
|
@@ -1773,15 +1768,6 @@ function handleKeyPress(event, elementItems, elementItemIndex, menuElementDiv, t
|
|
|
1773
1768
|
}
|
|
1774
1769
|
case "Escape": {
|
|
1775
1770
|
event.preventDefault();
|
|
1776
|
-
if (menuElementDiv && triggerButton) {
|
|
1777
|
-
if (getComputedStyle(menuElementDiv).display === "block") {
|
|
1778
|
-
handleMenuClose(menuElementDiv, triggerButton);
|
|
1779
|
-
if (onOpenChange) {
|
|
1780
|
-
onOpenChange(false);
|
|
1781
|
-
}
|
|
1782
|
-
}
|
|
1783
|
-
triggerButton.focus();
|
|
1784
|
-
}
|
|
1785
1771
|
break;
|
|
1786
1772
|
}
|
|
1787
1773
|
case "Enter":
|
|
@@ -1796,12 +1782,6 @@ function handleKeyPress(event, elementItems, elementItemIndex, menuElementDiv, t
|
|
|
1796
1782
|
break;
|
|
1797
1783
|
}
|
|
1798
1784
|
case "Tab": {
|
|
1799
|
-
if (menuElementDiv && triggerButton && (!event.shiftKey || event.shiftKey)) {
|
|
1800
|
-
handleMenuClose(menuElementDiv, triggerButton);
|
|
1801
|
-
if (onOpenChange) {
|
|
1802
|
-
onOpenChange(false);
|
|
1803
|
-
}
|
|
1804
|
-
}
|
|
1805
1785
|
break;
|
|
1806
1786
|
}
|
|
1807
1787
|
default:
|
|
@@ -2035,12 +2015,92 @@ function makeMenuAccessible({ menuId, menuItemsClass, triggerId, callback }) {
|
|
|
2035
2015
|
};
|
|
2036
2016
|
return nodeListLike;
|
|
2037
2017
|
}
|
|
2018
|
+
function intializeMenuItems() {
|
|
2019
|
+
const items = getItems();
|
|
2020
|
+
items.forEach((item) => {
|
|
2021
|
+
item.setAttribute("role", "menuitem");
|
|
2022
|
+
if (item.hasAttribute("data-submenu-id")) {
|
|
2023
|
+
item.setAttribute("aria-haspopup", "menu");
|
|
2024
|
+
item.setAttribute("aria-controls", item.getAttribute("data-submenu-id"));
|
|
2025
|
+
}
|
|
2026
|
+
});
|
|
2027
|
+
}
|
|
2028
|
+
function moveFocus2(elementItems, currentIndex, direction) {
|
|
2029
|
+
const len = elementItems.length;
|
|
2030
|
+
const nextIndex = (currentIndex + direction + len) % len;
|
|
2031
|
+
elementItems.item(nextIndex).focus();
|
|
2032
|
+
}
|
|
2033
|
+
function hasSubmenu(menuItem) {
|
|
2034
|
+
return menuItem.hasAttribute("aria-controls") && menuItem.hasAttribute("aria-haspopup") && menuItem.getAttribute("role") === "menuitem";
|
|
2035
|
+
}
|
|
2036
|
+
intializeMenuItems();
|
|
2037
|
+
function handleItemsKeydown(event, menuItem, menuItemIndex) {
|
|
2038
|
+
switch (event.key) {
|
|
2039
|
+
case "ArrowUp":
|
|
2040
|
+
case "ArrowLeft": {
|
|
2041
|
+
if (event.key === "ArrowLeft" && triggerButton.getAttribute("role") === "menuitem") {
|
|
2042
|
+
event.preventDefault();
|
|
2043
|
+
closeMenu();
|
|
2044
|
+
return;
|
|
2045
|
+
}
|
|
2046
|
+
event.preventDefault();
|
|
2047
|
+
moveFocus2(toNodeListLike(getFilteredItems()), menuItemIndex, -1);
|
|
2048
|
+
break;
|
|
2049
|
+
}
|
|
2050
|
+
case "ArrowDown":
|
|
2051
|
+
case "ArrowRight": {
|
|
2052
|
+
if (event.key === "ArrowRight" && hasSubmenu(menuItem)) {
|
|
2053
|
+
event.preventDefault();
|
|
2054
|
+
const submenuId = menuItem.getAttribute("aria-controls");
|
|
2055
|
+
if (submenuId) {
|
|
2056
|
+
openSubmenu(submenuId);
|
|
2057
|
+
return;
|
|
2058
|
+
}
|
|
2059
|
+
}
|
|
2060
|
+
event.preventDefault();
|
|
2061
|
+
moveFocus2(toNodeListLike(getFilteredItems()), menuItemIndex, 1);
|
|
2062
|
+
break;
|
|
2063
|
+
}
|
|
2064
|
+
case "Escape": {
|
|
2065
|
+
event.preventDefault();
|
|
2066
|
+
closeMenu();
|
|
2067
|
+
triggerButton.focus();
|
|
2068
|
+
if (onOpenChange) {
|
|
2069
|
+
onOpenChange(false);
|
|
2070
|
+
}
|
|
2071
|
+
break;
|
|
2072
|
+
}
|
|
2073
|
+
case "Enter":
|
|
2074
|
+
case " ": {
|
|
2075
|
+
event.preventDefault();
|
|
2076
|
+
menuItem.click();
|
|
2077
|
+
break;
|
|
2078
|
+
}
|
|
2079
|
+
case "Tab": {
|
|
2080
|
+
if (!event.shiftKey || event.shiftKey) {
|
|
2081
|
+
closeMenu();
|
|
2082
|
+
if (onOpenChange) {
|
|
2083
|
+
onOpenChange(false);
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
break;
|
|
2087
|
+
}
|
|
2088
|
+
default:
|
|
2089
|
+
break;
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2038
2092
|
function isItemInNestedSubmenu(item) {
|
|
2039
2093
|
let parent = item.parentElement;
|
|
2040
2094
|
while (parent && parent !== menuDiv) {
|
|
2041
2095
|
if (parent.getAttribute("role") === "menu") {
|
|
2042
2096
|
return true;
|
|
2043
2097
|
}
|
|
2098
|
+
if (parent.id) {
|
|
2099
|
+
const parentMenuTrigger = menuDiv.querySelector(`[aria-controls="${parent.id}"]`);
|
|
2100
|
+
if (parentMenuTrigger) {
|
|
2101
|
+
return true;
|
|
2102
|
+
}
|
|
2103
|
+
}
|
|
2044
2104
|
parent = parent.parentElement;
|
|
2045
2105
|
}
|
|
2046
2106
|
return false;
|
|
@@ -2076,9 +2136,6 @@ function makeMenuAccessible({ menuId, menuItemsClass, triggerId, callback }) {
|
|
|
2076
2136
|
}
|
|
2077
2137
|
submenuInstance.openMenu();
|
|
2078
2138
|
}
|
|
2079
|
-
function closeSubmenu() {
|
|
2080
|
-
closeMenu();
|
|
2081
|
-
}
|
|
2082
2139
|
function onOpenChange(isOpen) {
|
|
2083
2140
|
if (callback?.onOpenChange) {
|
|
2084
2141
|
try {
|
|
@@ -2090,19 +2147,9 @@ function makeMenuAccessible({ menuId, menuItemsClass, triggerId, callback }) {
|
|
|
2090
2147
|
}
|
|
2091
2148
|
function addListeners() {
|
|
2092
2149
|
const items = getFilteredItems();
|
|
2093
|
-
const nodeListLike = toNodeListLike(items);
|
|
2094
2150
|
items.forEach((menuItem, index) => {
|
|
2095
2151
|
if (!handlerMap.has(menuItem)) {
|
|
2096
|
-
const handler = (event) =>
|
|
2097
|
-
event,
|
|
2098
|
-
nodeListLike,
|
|
2099
|
-
index,
|
|
2100
|
-
menuDiv,
|
|
2101
|
-
triggerButton,
|
|
2102
|
-
openSubmenu,
|
|
2103
|
-
closeSubmenu,
|
|
2104
|
-
onOpenChange
|
|
2105
|
-
);
|
|
2152
|
+
const handler = (event) => handleItemsKeydown(event, menuItem, index);
|
|
2106
2153
|
menuItem.addEventListener("keydown", handler);
|
|
2107
2154
|
handlerMap.set(menuItem, handler);
|
|
2108
2155
|
}
|
|
@@ -2147,13 +2194,6 @@ function makeMenuAccessible({ menuId, menuItemsClass, triggerId, callback }) {
|
|
|
2147
2194
|
}
|
|
2148
2195
|
}
|
|
2149
2196
|
}
|
|
2150
|
-
function intializeMenuItems() {
|
|
2151
|
-
const items = getItems();
|
|
2152
|
-
items.forEach((item) => {
|
|
2153
|
-
item.setAttribute("role", "menuitem");
|
|
2154
|
-
});
|
|
2155
|
-
}
|
|
2156
|
-
intializeMenuItems();
|
|
2157
2197
|
function handleTriggerClick() {
|
|
2158
2198
|
const isOpen = triggerButton.getAttribute("aria-expanded") === "true";
|
|
2159
2199
|
if (isOpen) {
|
|
@@ -2929,7 +2969,7 @@ function makeTabsAccessible({ tabListId, tabsClass, tabPanelsClass, orientation
|
|
|
2929
2969
|
// src/utils/test/src/test.ts
|
|
2930
2970
|
var import_jest_axe = require("jest-axe");
|
|
2931
2971
|
|
|
2932
|
-
// src/utils/test/
|
|
2972
|
+
// src/utils/test/src/contractTestRunner.ts
|
|
2933
2973
|
init_contract();
|
|
2934
2974
|
var import_promises = __toESM(require("fs/promises"), 1);
|
|
2935
2975
|
init_ContractReporter();
|
|
@@ -2949,6 +2989,7 @@ async function runContractTests(componentName, component) {
|
|
|
2949
2989
|
const failures = [];
|
|
2950
2990
|
const passes = [];
|
|
2951
2991
|
const skipped = [];
|
|
2992
|
+
const failuresBeforeStatic = failures.length;
|
|
2952
2993
|
for (const test of componentContract.static[0].assertions) {
|
|
2953
2994
|
if (test.target !== "relative") {
|
|
2954
2995
|
const selector = componentContract.selectors[test.target];
|
|
@@ -2987,8 +3028,9 @@ async function runContractTests(componentName, component) {
|
|
|
2987
3028
|
skipped.push(dynamicTest.description);
|
|
2988
3029
|
reporter.reportTest(dynamicTest, "skip");
|
|
2989
3030
|
}
|
|
2990
|
-
const
|
|
2991
|
-
const staticFailed =
|
|
3031
|
+
const staticTotal = componentContract.static[0].assertions.length;
|
|
3032
|
+
const staticFailed = failures.length - failuresBeforeStatic;
|
|
3033
|
+
const staticPassed = Math.max(0, staticTotal - staticFailed);
|
|
2992
3034
|
reporter.reportStatic(staticPassed, staticFailed);
|
|
2993
3035
|
reporter.summary(failures);
|
|
2994
3036
|
return { passes, failures, skipped };
|