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
|
@@ -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/
|
|
38
|
+
path: "./aria-contracts/menu/menu.contract.json",
|
|
39
39
|
component: "menu"
|
|
40
40
|
},
|
|
41
|
-
combobox: {
|
|
42
|
-
path: "./contracts/
|
|
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/
|
|
46
|
+
path: "./aria-contracts/accordion/accordion.contract.json",
|
|
47
47
|
component: "accordion"
|
|
48
48
|
},
|
|
49
49
|
tabs: {
|
|
50
|
-
path: "./contracts/
|
|
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/
|
|
57
|
+
// src/utils/test/src/ContractReporter.ts
|
|
58
58
|
var ContractReporter;
|
|
59
59
|
var init_ContractReporter = __esm({
|
|
60
|
-
"src/utils/test/
|
|
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}',
|
|
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/
|
|
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
|
|
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/
|
|
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:
|
|
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: "
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1286
|
-
const staticFailed =
|
|
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/
|
|
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/
|
|
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
|
|
1491
|
-
const staticFailed =
|
|
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-
|
|
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
|
|
59
|
-
const staticFailed =
|
|
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-
|
|
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.
|
|
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 };
|