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
@@ -35,29 +35,29 @@ var init_contract = __esm({
35
35
  "src/utils/test/contract/contract.json"() {
36
36
  contract_default = {
37
37
  menu: {
38
- path: "./contracts/MenuContract.json",
38
+ path: "./aria-contracts/menu/menu.contract.json",
39
39
  component: "menu"
40
40
  },
41
- combobox: {
42
- path: "./contracts/ComboboxContract.json",
43
- component: "combobox"
41
+ "combobox.listbox": {
42
+ path: "./aria-contracts/combobox/combobox.listbox.contract.json",
43
+ component: "combobox.listbox"
44
44
  },
45
45
  accordion: {
46
- path: "./contracts/AccordionContract.json",
46
+ path: "./aria-contracts/accordion/accordion.contract.json",
47
47
  component: "accordion"
48
48
  },
49
49
  tabs: {
50
- path: "./contracts/TabsContract.json",
50
+ path: "./aria-contracts/tabs/tabs.contract.json",
51
51
  component: "tabs"
52
52
  }
53
53
  };
54
54
  }
55
55
  });
56
56
 
57
- // src/utils/test/contract/ContractReporter.ts
57
+ // src/utils/test/src/ContractReporter.ts
58
58
  var ContractReporter;
59
59
  var init_ContractReporter = __esm({
60
- "src/utils/test/contract/ContractReporter.ts"() {
60
+ "src/utils/test/src/ContractReporter.ts"() {
61
61
  ContractReporter = class {
62
62
  startTime = 0;
63
63
  componentName = "";
@@ -69,6 +69,8 @@ var init_ContractReporter = __esm({
69
69
  optionalSuggestions = 0;
70
70
  isPlaywright = false;
71
71
  apgUrl = "https://www.w3.org/WAI/ARIA/apg/";
72
+ hasPrintedStaticSection = false;
73
+ hasPrintedDynamicSection = false;
72
74
  constructor(isPlaywright = false) {
73
75
  this.isPlaywright = isPlaywright;
74
76
  }
@@ -79,30 +81,32 @@ var init_ContractReporter = __esm({
79
81
  this.startTime = Date.now();
80
82
  this.componentName = componentName;
81
83
  this.totalTests = totalTests;
84
+ this.hasPrintedStaticSection = false;
85
+ this.hasPrintedDynamicSection = false;
82
86
  if (apgUrl) {
83
87
  this.apgUrl = apgUrl;
84
88
  }
85
89
  const mode = this.isPlaywright ? "Playwright (Real Browser)" : "jsdom (Fast)";
86
90
  this.log(`
87
91
  ${"\u2550".repeat(60)}`);
88
- this.log(`\u{1F50D} Testing ${componentName} Component - ${mode}`);
92
+ this.log(`\u{1F50D} Testing ${componentName.charAt(0).toUpperCase() + componentName.slice(1)} Component - ${mode}`);
89
93
  this.log(`${"\u2550".repeat(60)}
90
94
  `);
91
95
  }
92
96
  reportStatic(passes, failures) {
93
97
  this.staticPasses = passes;
94
98
  this.staticFailures = failures;
95
- const icon = failures === 0 ? "\u2705" : "\u274C";
96
- const status = failures === 0 ? "PASS" : "FAIL";
97
- this.log("");
98
- this.log(`${icon} Static ARIA Tests: ${status}`);
99
- this.log(` ${passes}/${passes + failures} required attributes present
100
- `);
101
99
  }
102
100
  /**
103
101
  * Report individual static test pass
104
102
  */
105
103
  reportStaticTest(description, passed, failureMessage) {
104
+ if (!this.hasPrintedStaticSection) {
105
+ this.log(`${"\u2500".repeat(60)}`);
106
+ this.log(`\u{1F9EA} Static Assertions`);
107
+ this.log(`${"\u2500".repeat(60)}`);
108
+ this.hasPrintedStaticSection = true;
109
+ }
106
110
  const icon = passed ? "\u2713" : "\u2717";
107
111
  this.log(` ${icon} ${description}`);
108
112
  if (!passed && failureMessage) {
@@ -113,6 +117,13 @@ ${"\u2550".repeat(60)}`);
113
117
  * Report individual dynamic test result
114
118
  */
115
119
  reportTest(test, status, failureMessage) {
120
+ if (!this.hasPrintedDynamicSection) {
121
+ this.log("");
122
+ this.log(`${"\u2500".repeat(60)}`);
123
+ this.log(`\u2328\uFE0F Dynamic Interaction Tests`);
124
+ this.log(`${"\u2500".repeat(60)}`);
125
+ this.hasPrintedDynamicSection = true;
126
+ }
116
127
  const result = {
117
128
  description: test.description,
118
129
  status,
@@ -199,7 +210,7 @@ ${"\u2500".repeat(60)}`);
199
210
  });
200
211
  this.log(`
201
212
  \u{1F4A1} Run with Playwright for full validation:`);
202
- this.log(` testUiComponent('${this.componentName}', component, 'http://localhost:5173/')
213
+ this.log(` testUiComponent('${this.componentName}', null, 'http://localhost:5173/test-harness?component=component_name')
203
214
  `);
204
215
  }
205
216
  /**
@@ -223,9 +234,14 @@ ${"\u2500".repeat(60)}`);
223
234
  ${"\u2550".repeat(60)}`);
224
235
  this.log(`\u{1F4CA} Summary
225
236
  `);
237
+ const staticIcon = this.staticFailures === 0 ? "\u2705" : "\u274C";
238
+ const staticStatus = this.staticFailures === 0 ? "PASS" : "FAIL";
239
+ this.log(`${staticIcon} Static ARIA Tests: ${staticStatus}`);
240
+ this.log(` ${this.staticPasses}/${this.staticPasses + this.staticFailures} required attributes present`);
241
+ this.log("");
226
242
  if (totalFailures === 0 && this.skipped === 0 && this.optionalSuggestions === 0) {
227
243
  this.log(`\u2705 All ${totalRun} tests passed!`);
228
- this.log(` ${this.componentName} component meets WAI-ARIA expectations for Roles, States, Properties, and Keyboard Interactions \u2713`);
244
+ this.log(` ${this.componentName.charAt(0).toUpperCase()}${this.componentName.slice(1)} component meets WAI-ARIA expectations for Roles, States, Properties, and Keyboard Interactions \u2713`);
229
245
  } else if (totalFailures === 0) {
230
246
  this.log(`\u2705 ${totalPasses}/${totalRun} required tests passed`);
231
247
  if (this.skipped > 0) {
@@ -234,7 +250,7 @@ ${"\u2550".repeat(60)}`);
234
250
  if (this.optionalSuggestions > 0) {
235
251
  this.log(`\u{1F4A1} ${this.optionalSuggestions} optional enhancement${this.optionalSuggestions > 1 ? "s" : ""} suggested`);
236
252
  }
237
- this.log(` ${this.componentName} component meets WAI-ARIA expectations for Roles, States, Properties, and Keyboard Interactions \u2713`);
253
+ this.log(` ${this.componentName.charAt(0).toUpperCase()}${this.componentName.slice(1)} component meets WAI-ARIA expectations for Roles, States, Properties, and Keyboard Interactions \u2713`);
238
254
  } else {
239
255
  this.log(`\u274C ${totalFailures} test${totalFailures > 1 ? "s" : ""} failed`);
240
256
  this.log(`\u2705 ${totalPasses} test${totalPasses > 1 ? "s" : ""} passed`);
@@ -320,7 +336,7 @@ async function closeSharedBrowser() {
320
336
  }
321
337
  var sharedBrowser, sharedContext;
322
338
  var init_playwrightTestHarness = __esm({
323
- "src/utils/test/contract/playwrightTestHarness.ts"() {
339
+ "src/utils/test/src/playwrightTestHarness.ts"() {
324
340
  sharedBrowser = null;
325
341
  sharedContext = null;
326
342
  }
@@ -558,7 +574,7 @@ var init_ComponentDetector = __esm({
558
574
  const contractData = fs.readFileSync(resolvedPath, "utf-8");
559
575
  const componentContract = JSON.parse(contractData);
560
576
  const selectors = componentContract.selectors;
561
- if (componentName === "combobox") {
577
+ if (componentName.includes("combobox")) {
562
578
  const mainSelector = selectors.input || selectors.container;
563
579
  return new ComboboxComponentStrategy(mainSelector, selectors, actionTimeoutMs, assertionTimeoutMs);
564
580
  }
@@ -1074,7 +1090,7 @@ var init_AssertionRunner = __esm({
1074
1090
  }
1075
1091
  });
1076
1092
 
1077
- // src/utils/test/contract/contractTestRunnerPlaywright.ts
1093
+ // src/utils/test/src/contractTestRunnerPlaywright.ts
1078
1094
  var contractTestRunnerPlaywright_exports = {};
1079
1095
  __export(contractTestRunnerPlaywright_exports, {
1080
1096
  runContractTestsPlaywright: () => runContractTestsPlaywright
@@ -1118,7 +1134,7 @@ async function runContractTestsPlaywright(componentName, url) {
1118
1134
  throw new Error(`CRITICAL: No selector found in contract for ${componentName}`);
1119
1135
  }
1120
1136
  try {
1121
- await page.locator(mainSelector).first().waitFor({ state: "attached", timeout: 28e3 });
1137
+ await page.locator(mainSelector).first().waitFor({ state: "attached", timeout: 3e4 });
1122
1138
  } catch (error) {
1123
1139
  throw new Error(
1124
1140
  `
@@ -1133,24 +1149,29 @@ This usually means:
1133
1149
  reporter.start(componentName, totalTests, apgUrl);
1134
1150
  if (componentName === "menu" && componentContract.selectors.trigger) {
1135
1151
  await page.locator(componentContract.selectors.trigger).first().waitFor({
1136
- state: "visible",
1152
+ state: "attached",
1137
1153
  timeout: 5e3
1138
1154
  }).catch(() => {
1139
- console.warn("Menu trigger not visible, continuing with tests...");
1140
1155
  });
1141
1156
  }
1157
+ const failuresBeforeStatic = failures.length;
1142
1158
  const staticAssertionRunner = new AssertionRunner(page, componentContract.selectors, assertionTimeoutMs);
1143
1159
  for (const test of componentContract.static[0]?.assertions || []) {
1144
1160
  if (test.target === "relative") continue;
1161
+ const staticDescription = `${test.target}${test.attribute ? ` (${test.attribute})` : ""}`;
1145
1162
  const targetSelector = componentContract.selectors[test.target];
1146
1163
  if (!targetSelector) {
1147
- failures.push(`Selector for target ${test.target} not found.`);
1164
+ const failure = `Selector for target ${test.target} not found.`;
1165
+ failures.push(failure);
1166
+ reporter.reportStaticTest(staticDescription, false, failure);
1148
1167
  continue;
1149
1168
  }
1150
1169
  const target = page.locator(targetSelector).first();
1151
1170
  const exists = await target.count() > 0;
1152
1171
  if (!exists) {
1153
- failures.push(`Target ${test.target} not found.`);
1172
+ const failure = `Target ${test.target} not found.`;
1173
+ failures.push(failure);
1174
+ reporter.reportStaticTest(staticDescription, false, failure);
1154
1175
  continue;
1155
1176
  }
1156
1177
  const isRedundantCheck = (selector, attrName, expectedVal) => {
@@ -1184,13 +1205,19 @@ This usually means:
1184
1205
  }
1185
1206
  }
1186
1207
  if (!hasAny && !allRedundant) {
1187
- failures.push(test.failureMessage + ` None of the attributes "${test.attribute}" are present.`);
1208
+ const failure = test.failureMessage + ` None of the attributes "${test.attribute}" are present.`;
1209
+ failures.push(failure);
1210
+ reporter.reportStaticTest(staticDescription, false, failure);
1188
1211
  } else if (!allRedundant && hasAny) {
1189
1212
  passes.push(`At least one of the attributes "${test.attribute}" exists on the element.`);
1213
+ reporter.reportStaticTest(staticDescription, true);
1214
+ } else {
1215
+ reporter.reportStaticTest(staticDescription, true);
1190
1216
  }
1191
1217
  } else {
1192
1218
  if (isRedundantCheck(targetSelector, test.attribute, test.expectedValue)) {
1193
1219
  passes.push(`${test.attribute}="${test.expectedValue}" on ${test.target} verified by selector (already present in: ${targetSelector}).`);
1220
+ reporter.reportStaticTest(staticDescription, true);
1194
1221
  } else {
1195
1222
  const result = await staticAssertionRunner.validateAttribute(
1196
1223
  target,
@@ -1202,8 +1229,10 @@ This usually means:
1202
1229
  );
1203
1230
  if (result.success && result.passMessage) {
1204
1231
  passes.push(result.passMessage);
1232
+ reporter.reportStaticTest(staticDescription, true);
1205
1233
  } else if (!result.success && result.failMessage) {
1206
1234
  failures.push(result.failMessage);
1235
+ reporter.reportStaticTest(staticDescription, false, result.failMessage);
1207
1236
  }
1208
1237
  }
1209
1238
  }
@@ -1282,8 +1311,9 @@ This usually means:
1282
1311
  reporter.reportTest(dynamicTest, testPassed ? "pass" : "fail", failureMessage);
1283
1312
  }
1284
1313
  }
1285
- const staticPassed = componentContract.static[0].assertions.length;
1286
- const staticFailed = 0;
1314
+ const staticTotal = componentContract.static[0].assertions.length;
1315
+ const staticFailed = failures.length - failuresBeforeStatic;
1316
+ const staticPassed = Math.max(0, staticTotal - staticFailed);
1287
1317
  reporter.reportStatic(staticPassed, staticFailed);
1288
1318
  reporter.summary(failures);
1289
1319
  } catch (error) {
@@ -1312,7 +1342,7 @@ Make sure your dev server is running at ${url}`);
1312
1342
  return { passes, failures, skipped };
1313
1343
  }
1314
1344
  var init_contractTestRunnerPlaywright = __esm({
1315
- "src/utils/test/contract/contractTestRunnerPlaywright.ts"() {
1345
+ "src/utils/test/src/contractTestRunnerPlaywright.ts"() {
1316
1346
  init_contract();
1317
1347
  init_playwrightTestHarness();
1318
1348
  init_ComponentDetector();
@@ -1431,7 +1461,7 @@ var init_badgeHelper = __esm({
1431
1461
  }
1432
1462
  });
1433
1463
 
1434
- // src/utils/test/contract/contractTestRunner.ts
1464
+ // src/utils/test/src/contractTestRunner.ts
1435
1465
  init_contract();
1436
1466
  init_ContractReporter();
1437
1467
  async function runContractTests(componentName, component) {
@@ -1449,6 +1479,7 @@ async function runContractTests(componentName, component) {
1449
1479
  const failures = [];
1450
1480
  const passes = [];
1451
1481
  const skipped = [];
1482
+ const failuresBeforeStatic = failures.length;
1452
1483
  for (const test of componentContract.static[0].assertions) {
1453
1484
  if (test.target !== "relative") {
1454
1485
  const selector = componentContract.selectors[test.target];
@@ -1487,8 +1518,9 @@ async function runContractTests(componentName, component) {
1487
1518
  skipped.push(dynamicTest.description);
1488
1519
  reporter.reportTest(dynamicTest, "skip");
1489
1520
  }
1490
- const staticPassed = componentContract.static[0].assertions.length;
1491
- const staticFailed = 0;
1521
+ const staticTotal = componentContract.static[0].assertions.length;
1522
+ const staticFailed = failures.length - failuresBeforeStatic;
1523
+ const staticPassed = Math.max(0, staticTotal - staticFailed);
1492
1524
  reporter.reportStatic(staticPassed, staticFailed);
1493
1525
  reporter.summary(failures);
1494
1526
  return { passes, failures, skipped };
@@ -1,4 +1,4 @@
1
- import { closeSharedBrowser, ContractReporter, contract_default } from './chunk-GFKAJHCS.js';
1
+ import { closeSharedBrowser, ContractReporter, contract_default } from './chunk-AUJAN4RK.js';
2
2
  import { axe } from 'jest-axe';
3
3
  import fs from 'fs/promises';
4
4
 
@@ -17,6 +17,7 @@ async function runContractTests(componentName, component) {
17
17
  const failures = [];
18
18
  const passes = [];
19
19
  const skipped = [];
20
+ const failuresBeforeStatic = failures.length;
20
21
  for (const test of componentContract.static[0].assertions) {
21
22
  if (test.target !== "relative") {
22
23
  const selector = componentContract.selectors[test.target];
@@ -55,8 +56,9 @@ async function runContractTests(componentName, component) {
55
56
  skipped.push(dynamicTest.description);
56
57
  reporter.reportTest(dynamicTest, "skip");
57
58
  }
58
- const staticPassed = componentContract.static[0].assertions.length;
59
- const staticFailed = 0;
59
+ const staticTotal = componentContract.static[0].assertions.length;
60
+ const staticFailed = failures.length - failuresBeforeStatic;
61
+ const staticPassed = Math.max(0, staticTotal - staticFailed);
60
62
  reporter.reportStatic(staticPassed, staticFailed);
61
63
  reporter.summary(failures);
62
64
  return { passes, failures, skipped };
@@ -106,7 +108,7 @@ Error: ${error instanceof Error ? error.message : String(error)}`
106
108
  const devServerUrl = await checkDevServer(url);
107
109
  if (devServerUrl) {
108
110
  console.log(`\u{1F3AD} Running Playwright tests on ${devServerUrl}`);
109
- const { runContractTestsPlaywright } = await import('./contractTestRunnerPlaywright-O7FF3X67.js');
111
+ const { runContractTestsPlaywright } = await import('./contractTestRunnerPlaywright-HL73FADJ.js');
110
112
  contract = await runContractTestsPlaywright(componentName, devServerUrl);
111
113
  } else {
112
114
  throw new Error(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aria-ease",
3
- "version": "6.4.10",
3
+ "version": "6.5.1",
4
4
  "description": "Accessibility infrastructure for the entire frontend engineering lifecycle. Build accessible patterns, run automated audits, verify component interactions, and gate deployments — all in one system.",
5
5
  "main": "dist/index.cjs",
6
6
  "type": "module",
@@ -21,7 +21,7 @@
21
21
  "build:modules": "tsup ./src/combobox/index.ts ./src/accordion/index.ts ./src/block/index.ts ./src/checkbox/index.ts ./src/menu/index.ts ./src/tabs/index.ts ./src/radio/index.ts ./src/toggle/index.ts --format esm,cjs --dts --treeshake --outDir dist/src",
22
22
  "build:test": "tsup ./src/utils/test/index.ts --format esm,cjs --dts --treeshake --external jest-axe --external @testing-library/react --external playwright --external @playwright/test --outDir dist/src/utils/test",
23
23
  "build:cli": "tsup ./src/utils/cli/cli.ts --format esm,cjs --dts --outDir bin --external commander --external chalk --external jest-axe --external @testing-library/react --external @axe-core/playwright --external playwright",
24
- "build:contracts": "mkdir -p ./dist/src/utils/test && cp -r ./src/utils/test/contract/contracts ./dist/src/utils/test/",
24
+ "build:contracts": "mkdir -p ./dist/src/utils/test && cp -r ./src/utils/test/contract/aria-contracts ./dist/src/utils/test/",
25
25
  "build": "npm run clean && npm run build:core && npm run build:modules && npm run build:test && npm run build:cli && npm run build:contracts"
26
26
  },
27
27
  "repository": {
@@ -1,127 +0,0 @@
1
- // src/utils/handleKeyPress/handleKeyPress.ts
2
- function isTextInput(el) {
3
- if (el.tagName !== "INPUT") return false;
4
- const type = el.type;
5
- return ["text", "email", "password", "tel", "number"].includes(type);
6
- }
7
- function isTextArea(el) {
8
- return el.tagName === "TEXTAREA";
9
- }
10
- function isNativeButton(el) {
11
- return el.tagName === "BUTTON" || el.tagName === "INPUT" && ["button", "submit", "reset"].includes(el.type);
12
- }
13
- function isLink(el) {
14
- return el.tagName === "A";
15
- }
16
- function moveFocus(elementItems, currentIndex, direction) {
17
- const len = elementItems.length;
18
- const nextIndex = (currentIndex + direction + len) % len;
19
- elementItems.item(nextIndex).focus();
20
- }
21
- function isClickableButNotSemantic(el) {
22
- return el.getAttribute("data-custom-click") !== null && el.getAttribute("data-custom-click") !== void 0;
23
- }
24
- function handleMenuClose(menuElement, menuTriggerButton) {
25
- menuElement.style.display = "none";
26
- const menuTriggerButtonId = menuTriggerButton.getAttribute("id");
27
- if (!menuTriggerButtonId) {
28
- console.error("[aria-ease] Menu trigger button must have an id attribute to properly set aria attributes.");
29
- return;
30
- }
31
- menuTriggerButton.setAttribute("aria-expanded", "false");
32
- }
33
- function hasSubmenu(menuItem) {
34
- return menuItem.getAttribute("aria-haspopup") === "true" || menuItem.getAttribute("aria-haspopup") === "menu";
35
- }
36
- function getSubmenuId(menuItem) {
37
- return menuItem.getAttribute("aria-controls");
38
- }
39
- function handleKeyPress(event, elementItems, elementItemIndex, menuElementDiv, triggerButton, openSubmenu, closeSubmenu, onOpenChange) {
40
- const currentEl = elementItems.item(elementItemIndex);
41
- switch (event.key) {
42
- case "ArrowUp":
43
- case "ArrowLeft": {
44
- if (event.key === "ArrowLeft" && menuElementDiv && closeSubmenu) {
45
- const labelledBy = menuElementDiv.getAttribute("aria-labelledby");
46
- if (labelledBy) {
47
- const parentTrigger = document.getElementById(labelledBy);
48
- if (parentTrigger && parentTrigger.getAttribute("role") === "menuitem") {
49
- event.preventDefault();
50
- closeSubmenu();
51
- parentTrigger.focus();
52
- return;
53
- }
54
- }
55
- }
56
- if (!isTextInput(currentEl) && !isTextArea(currentEl)) {
57
- event.preventDefault();
58
- moveFocus(elementItems, elementItemIndex, -1);
59
- } else if (isTextInput(currentEl) || isTextArea(currentEl)) {
60
- const cursorStart = currentEl.selectionStart;
61
- if (cursorStart === 0) {
62
- event.preventDefault();
63
- moveFocus(elementItems, elementItemIndex, -1);
64
- }
65
- }
66
- break;
67
- }
68
- case "ArrowDown":
69
- case "ArrowRight": {
70
- if (event.key === "ArrowRight" && hasSubmenu(currentEl) && openSubmenu) {
71
- event.preventDefault();
72
- const submenuId = getSubmenuId(currentEl);
73
- if (submenuId) {
74
- openSubmenu(submenuId);
75
- return;
76
- }
77
- }
78
- if (!isTextInput(currentEl) && !isTextArea(currentEl)) {
79
- event.preventDefault();
80
- moveFocus(elementItems, elementItemIndex, 1);
81
- } else if (isTextInput(currentEl) || isTextArea(currentEl)) {
82
- const value = currentEl.value;
83
- const cursorEnd = currentEl.selectionStart;
84
- if (cursorEnd === value.length) {
85
- event.preventDefault();
86
- moveFocus(elementItems, elementItemIndex, 1);
87
- }
88
- }
89
- break;
90
- }
91
- case "Escape": {
92
- event.preventDefault();
93
- if (menuElementDiv && triggerButton) {
94
- if (getComputedStyle(menuElementDiv).display === "block") {
95
- handleMenuClose(menuElementDiv, triggerButton);
96
- if (onOpenChange) {
97
- onOpenChange(false);
98
- }
99
- }
100
- triggerButton.focus();
101
- }
102
- break;
103
- }
104
- case "Enter":
105
- case " ": {
106
- if (!isNativeButton(currentEl) && !isLink(currentEl) && isClickableButNotSemantic(currentEl)) {
107
- event.preventDefault();
108
- currentEl.click();
109
- } else if (isNativeButton(currentEl)) {
110
- event.preventDefault();
111
- currentEl.click();
112
- }
113
- break;
114
- }
115
- case "Tab": {
116
- if (menuElementDiv && triggerButton && (!event.shiftKey || event.shiftKey)) {
117
- handleMenuClose(menuElementDiv, triggerButton);
118
- if (onOpenChange) {
119
- onOpenChange(false);
120
- }
121
- }
122
- break;
123
- }
124
- }
125
- }
126
-
127
- export { handleKeyPress };