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.
Files changed (23) hide show
  1. package/bin/{chunk-GFKAJHCS.js → chunk-AUJAN4RK.js} +34 -18
  2. package/bin/cli.cjs +67 -35
  3. package/bin/cli.js +1 -1
  4. package/{dist/contractTestRunnerPlaywright-7BCEDPZF.js → bin/contractTestRunnerPlaywright-7F756CFB.js} +26 -12
  5. package/bin/{test-JGKWOL6J.js → test-C3CMRHSI.js} +7 -5
  6. package/dist/{chunk-GFKAJHCS.js → chunk-AUJAN4RK.js} +34 -18
  7. package/{bin/contractTestRunnerPlaywright-7BCEDPZF.js → dist/contractTestRunnerPlaywright-7F756CFB.js} +26 -12
  8. package/dist/index.cjs +148 -106
  9. package/dist/index.js +89 -77
  10. package/dist/src/block/index.cjs +1 -6
  11. package/dist/src/block/index.js +72 -1
  12. package/dist/src/menu/index.cjs +79 -147
  13. package/dist/src/menu/index.js +79 -23
  14. package/dist/src/utils/test/{contracts/AccordionContract.json → aria-contracts/accordion/accordion.contract.json} +21 -8
  15. package/dist/src/utils/test/{contracts/ComboboxContract.json → aria-contracts/combobox/combobox.listbox.contract.json} +18 -18
  16. package/dist/src/utils/test/{contracts/MenuContract.json → aria-contracts/menu/menu.contract.json} +43 -2
  17. package/dist/src/utils/test/{contracts/TabsContract.json → aria-contracts/tabs/tabs.contract.json} +21 -8
  18. package/dist/src/utils/test/{chunk-GFKAJHCS.js → chunk-AUJAN4RK.js} +33 -17
  19. package/dist/src/utils/test/{contractTestRunnerPlaywright-O7FF3X67.js → contractTestRunnerPlaywright-HL73FADJ.js} +25 -11
  20. package/dist/src/utils/test/index.cjs +65 -33
  21. package/dist/src/utils/test/index.js +6 -4
  22. package/package.json +2 -2
  23. 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/MenuContract.json",
40
+ path: "./aria-contracts/menu/menu.contract.json",
41
41
  component: "menu"
42
42
  },
43
- combobox: {
44
- path: "./contracts/ComboboxContract.json",
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/AccordionContract.json",
48
+ path: "./aria-contracts/accordion/accordion.contract.json",
49
49
  component: "accordion"
50
50
  },
51
51
  tabs: {
52
- path: "./contracts/TabsContract.json",
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/contract/ContractReporter.ts
59
+ // src/utils/test/src/ContractReporter.ts
60
60
  var ContractReporter;
61
61
  var init_ContractReporter = __esm({
62
- "src/utils/test/contract/ContractReporter.ts"() {
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}', component, 'http://localhost:5173/')
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/contract/playwrightTestHarness.ts
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/contract/playwrightTestHarness.ts"() {
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 === "combobox") {
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/contract/contractTestRunnerPlaywright.ts
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: 28e3 });
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: "visible",
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
- failures.push(`Selector for target ${test.target} not found.`);
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
- failures.push(`Target ${test.target} not found.`);
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
- failures.push(test.failureMessage + ` None of the attributes "${test.attribute}" are present.`);
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 staticPassed = componentContract.static[0].assertions.length;
1331
- const staticFailed = 0;
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/contract/contractTestRunnerPlaywright.ts"() {
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 handleMenuClose(menuElement, menuTriggerButton) {
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) => handleKeyPress(
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/contract/contractTestRunner.ts
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 staticPassed = componentContract.static[0].assertions.length;
2991
- const staticFailed = 0;
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 };