aria-ease 6.5.1 → 6.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -10
- package/bin/{chunk-AUJAN4RK.js → chunk-LKN5PRYD.js} +0 -5
- package/bin/cli.cjs +50 -33
- package/bin/cli.js +1 -1
- package/{dist/contractTestRunnerPlaywright-7F756CFB.js → bin/contractTestRunnerPlaywright-PC6JOYYV.js} +51 -29
- package/bin/{test-C3CMRHSI.js → test-LP723IXM.js} +2 -2
- package/dist/{chunk-AUJAN4RK.js → chunk-LKN5PRYD.js} +0 -5
- package/{bin/contractTestRunnerPlaywright-7F756CFB.js → dist/contractTestRunnerPlaywright-PC6JOYYV.js} +51 -29
- package/dist/index.cjs +94 -39
- package/dist/index.js +46 -8
- package/dist/src/menu/index.cjs +44 -6
- package/dist/src/menu/index.js +44 -6
- package/dist/src/utils/test/aria-contracts/menu/menu.contract.json +143 -30
- package/dist/src/utils/test/aria-contracts/tabs/tabs.contract.json +7 -7
- package/dist/src/utils/test/{chunk-AUJAN4RK.js → chunk-LKN5PRYD.js} +0 -5
- package/dist/src/utils/test/{contractTestRunnerPlaywright-HL73FADJ.js → contractTestRunnerPlaywright-RGKMGXND.js} +51 -29
- package/dist/src/utils/test/index.cjs +50 -33
- package/dist/src/utils/test/index.js +2 -2
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
ContractReporter,
|
|
3
3
|
contract_default,
|
|
4
4
|
createTestPage
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-LKN5PRYD.js";
|
|
6
6
|
import {
|
|
7
7
|
__export,
|
|
8
8
|
__reExport
|
|
@@ -166,29 +166,20 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
166
166
|
}
|
|
167
167
|
}
|
|
168
168
|
async shouldSkipTest(test, page) {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
}
|
|
169
|
+
const requiresSubmenu = test.action.some(
|
|
170
|
+
(act) => act.target === "submenu" || act.target === "submenuTrigger" || act.target === "submenuItems"
|
|
171
|
+
) || test.assertions.some(
|
|
172
|
+
(assertion) => assertion.target === "submenu" || assertion.target === "submenuTrigger" || assertion.target === "submenuItems"
|
|
173
|
+
);
|
|
174
|
+
if (!requiresSubmenu) {
|
|
175
|
+
return false;
|
|
179
176
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
if (submenuSelector) {
|
|
184
|
-
const submenuCount = await page.locator(submenuSelector).count();
|
|
185
|
-
if (submenuCount === 0) {
|
|
186
|
-
return true;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
}
|
|
177
|
+
const submenuTriggerSelector = this.selectors.submenuTrigger;
|
|
178
|
+
if (!submenuTriggerSelector) {
|
|
179
|
+
return true;
|
|
190
180
|
}
|
|
191
|
-
|
|
181
|
+
const submenuTriggerCount = await page.locator(submenuTriggerSelector).count();
|
|
182
|
+
return submenuTriggerCount === 0;
|
|
192
183
|
}
|
|
193
184
|
getMainSelector() {
|
|
194
185
|
return this.mainSelector;
|
|
@@ -300,6 +291,9 @@ var ActionExecutor = class {
|
|
|
300
291
|
this.selectors = selectors;
|
|
301
292
|
this.timeoutMs = timeoutMs;
|
|
302
293
|
}
|
|
294
|
+
isOptionalMenuTarget(target) {
|
|
295
|
+
return ["submenu", "submenuTrigger", "submenuItems"].includes(target);
|
|
296
|
+
}
|
|
303
297
|
/**
|
|
304
298
|
* Check if error is due to browser/page being closed
|
|
305
299
|
*/
|
|
@@ -420,7 +414,7 @@ var ActionExecutor = class {
|
|
|
420
414
|
} else if (keyValue.includes(" ")) {
|
|
421
415
|
keyValue = keyValue.replace(/ /g, "");
|
|
422
416
|
}
|
|
423
|
-
if (target === "focusable" && ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Escape"].includes(keyValue)) {
|
|
417
|
+
if (target === "focusable" && ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Escape", "Home", "End", "Tab", "Shift+Tab"].includes(keyValue)) {
|
|
424
418
|
await this.page.keyboard.press(keyValue);
|
|
425
419
|
return { success: true };
|
|
426
420
|
}
|
|
@@ -431,9 +425,10 @@ var ActionExecutor = class {
|
|
|
431
425
|
const locator = this.page.locator(selector).first();
|
|
432
426
|
const elementCount = await locator.count();
|
|
433
427
|
if (elementCount === 0) {
|
|
428
|
+
const optionalMenuTarget = this.isOptionalMenuTarget(target);
|
|
434
429
|
return {
|
|
435
430
|
success: false,
|
|
436
|
-
error: `${target} element not found (optional submenu test)
|
|
431
|
+
error: optionalMenuTarget ? `${target} element not found (optional submenu test)` : `${target} element not found.`,
|
|
437
432
|
shouldBreak: true
|
|
438
433
|
// Signal to skip this test
|
|
439
434
|
};
|
|
@@ -792,23 +787,35 @@ This usually means:
|
|
|
792
787
|
}).catch(() => {
|
|
793
788
|
});
|
|
794
789
|
}
|
|
795
|
-
const
|
|
790
|
+
const hasSubmenuCapability = componentName === "menu" && !!componentContract.selectors.submenuTrigger ? await page.locator(componentContract.selectors.submenuTrigger).count() > 0 : false;
|
|
791
|
+
let staticFailed = 0;
|
|
796
792
|
const staticAssertionRunner = new AssertionRunner(page, componentContract.selectors, assertionTimeoutMs);
|
|
797
793
|
for (const test of componentContract.static[0]?.assertions || []) {
|
|
798
794
|
if (test.target === "relative") continue;
|
|
799
795
|
const staticDescription = `${test.target}${test.attribute ? ` (${test.attribute})` : ""}`;
|
|
796
|
+
if (componentName === "menu" && test.target === "submenuTrigger" && !hasSubmenuCapability) {
|
|
797
|
+
passes.push(`Skipping submenu static assertion for ${test.target}: no submenu capability detected in rendered component.`);
|
|
798
|
+
reporter.reportStaticTest(staticDescription, true);
|
|
799
|
+
continue;
|
|
800
|
+
}
|
|
800
801
|
const targetSelector = componentContract.selectors[test.target];
|
|
801
802
|
if (!targetSelector) {
|
|
802
803
|
const failure = `Selector for target ${test.target} not found.`;
|
|
803
804
|
failures.push(failure);
|
|
805
|
+
staticFailed += 1;
|
|
804
806
|
reporter.reportStaticTest(staticDescription, false, failure);
|
|
805
807
|
continue;
|
|
806
808
|
}
|
|
807
809
|
const target = page.locator(targetSelector).first();
|
|
808
810
|
const exists = await target.count() > 0;
|
|
809
811
|
if (!exists) {
|
|
812
|
+
if (test.isOptional === true) {
|
|
813
|
+
reporter.reportStaticTest(staticDescription, true);
|
|
814
|
+
continue;
|
|
815
|
+
}
|
|
810
816
|
const failure = `Target ${test.target} not found.`;
|
|
811
817
|
failures.push(failure);
|
|
818
|
+
staticFailed += 1;
|
|
812
819
|
reporter.reportStaticTest(staticDescription, false, failure);
|
|
813
820
|
continue;
|
|
814
821
|
}
|
|
@@ -845,6 +852,7 @@ This usually means:
|
|
|
845
852
|
if (!hasAny && !allRedundant) {
|
|
846
853
|
const failure = test.failureMessage + ` None of the attributes "${test.attribute}" are present.`;
|
|
847
854
|
failures.push(failure);
|
|
855
|
+
staticFailed += 1;
|
|
848
856
|
reporter.reportStaticTest(staticDescription, false, failure);
|
|
849
857
|
} else if (!allRedundant && hasAny) {
|
|
850
858
|
passes.push(`At least one of the attributes "${test.attribute}" exists on the element.`);
|
|
@@ -870,6 +878,7 @@ This usually means:
|
|
|
870
878
|
reporter.reportStaticTest(staticDescription, true);
|
|
871
879
|
} else if (!result.success && result.failMessage) {
|
|
872
880
|
failures.push(result.failMessage);
|
|
881
|
+
staticFailed += 1;
|
|
873
882
|
reporter.reportStaticTest(staticDescription, false, result.failMessage);
|
|
874
883
|
}
|
|
875
884
|
}
|
|
@@ -899,9 +908,12 @@ This usually means:
|
|
|
899
908
|
}
|
|
900
909
|
const actionExecutor = new ActionExecutor(page, componentContract.selectors, actionTimeoutMs);
|
|
901
910
|
const assertionRunner = new AssertionRunner(page, componentContract.selectors, assertionTimeoutMs);
|
|
911
|
+
let shouldSkipCurrentTest = false;
|
|
912
|
+
let shouldAbortCurrentTest = false;
|
|
902
913
|
for (const act of action) {
|
|
903
914
|
if (!page || page.isClosed()) {
|
|
904
915
|
failures.push(`CRITICAL: Browser/page closed during test execution. Remaining actions skipped.`);
|
|
916
|
+
shouldAbortCurrentTest = true;
|
|
905
917
|
break;
|
|
906
918
|
}
|
|
907
919
|
let result;
|
|
@@ -919,18 +931,29 @@ This usually means:
|
|
|
919
931
|
continue;
|
|
920
932
|
}
|
|
921
933
|
if (!result.success) {
|
|
922
|
-
if (result.error) {
|
|
923
|
-
failures.push(result.error);
|
|
924
|
-
}
|
|
925
934
|
if (result.shouldBreak) {
|
|
926
935
|
if (result.error?.includes("optional submenu test")) {
|
|
927
936
|
reporter.reportTest(dynamicTest, "skip", result.error);
|
|
937
|
+
shouldSkipCurrentTest = true;
|
|
938
|
+
} else if (result.error) {
|
|
939
|
+
failures.push(result.error);
|
|
940
|
+
shouldAbortCurrentTest = true;
|
|
928
941
|
}
|
|
929
942
|
break;
|
|
930
943
|
}
|
|
944
|
+
if (result.error) {
|
|
945
|
+
failures.push(result.error);
|
|
946
|
+
}
|
|
931
947
|
continue;
|
|
932
948
|
}
|
|
933
949
|
}
|
|
950
|
+
if (shouldSkipCurrentTest) {
|
|
951
|
+
continue;
|
|
952
|
+
}
|
|
953
|
+
if (shouldAbortCurrentTest) {
|
|
954
|
+
reporter.reportTest(dynamicTest, "fail", failures[failures.length - 1]);
|
|
955
|
+
continue;
|
|
956
|
+
}
|
|
934
957
|
for (const assertion of assertions) {
|
|
935
958
|
const result = await assertionRunner.validate(assertion, dynamicTest.description);
|
|
936
959
|
if (result.success && result.passMessage) {
|
|
@@ -950,7 +973,6 @@ This usually means:
|
|
|
950
973
|
}
|
|
951
974
|
}
|
|
952
975
|
const staticTotal = componentContract.static[0].assertions.length;
|
|
953
|
-
const staticFailed = failures.length - failuresBeforeStatic;
|
|
954
976
|
const staticPassed = Math.max(0, staticTotal - staticFailed);
|
|
955
977
|
reporter.reportStatic(staticPassed, staticFailed);
|
|
956
978
|
reporter.summary(failures);
|
package/dist/index.cjs
CHANGED
|
@@ -237,11 +237,6 @@ ${"\u2500".repeat(60)}`);
|
|
|
237
237
|
${"\u2550".repeat(60)}`);
|
|
238
238
|
this.log(`\u{1F4CA} Summary
|
|
239
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("");
|
|
245
240
|
if (totalFailures === 0 && this.skipped === 0 && this.optionalSuggestions === 0) {
|
|
246
241
|
this.log(`\u2705 All ${totalRun} tests passed!`);
|
|
247
242
|
this.log(` ${this.componentName.charAt(0).toUpperCase()}${this.componentName.slice(1)} component meets WAI-ARIA expectations for Roles, States, Properties, and Keyboard Interactions \u2713`);
|
|
@@ -528,29 +523,20 @@ This indicates a problem with the menu component's close functionality.`
|
|
|
528
523
|
}
|
|
529
524
|
}
|
|
530
525
|
async shouldSkipTest(test, page) {
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
}
|
|
539
|
-
}
|
|
540
|
-
}
|
|
526
|
+
const requiresSubmenu = test.action.some(
|
|
527
|
+
(act) => act.target === "submenu" || act.target === "submenuTrigger" || act.target === "submenuItems"
|
|
528
|
+
) || test.assertions.some(
|
|
529
|
+
(assertion) => assertion.target === "submenu" || assertion.target === "submenuTrigger" || assertion.target === "submenuItems"
|
|
530
|
+
);
|
|
531
|
+
if (!requiresSubmenu) {
|
|
532
|
+
return false;
|
|
541
533
|
}
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
if (submenuSelector) {
|
|
546
|
-
const submenuCount = await page.locator(submenuSelector).count();
|
|
547
|
-
if (submenuCount === 0) {
|
|
548
|
-
return true;
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
}
|
|
534
|
+
const submenuTriggerSelector = this.selectors.submenuTrigger;
|
|
535
|
+
if (!submenuTriggerSelector) {
|
|
536
|
+
return true;
|
|
552
537
|
}
|
|
553
|
-
|
|
538
|
+
const submenuTriggerCount = await page.locator(submenuTriggerSelector).count();
|
|
539
|
+
return submenuTriggerCount === 0;
|
|
554
540
|
}
|
|
555
541
|
getMainSelector() {
|
|
556
542
|
return this.mainSelector;
|
|
@@ -693,6 +679,9 @@ var init_ActionExecutor = __esm({
|
|
|
693
679
|
this.selectors = selectors;
|
|
694
680
|
this.timeoutMs = timeoutMs;
|
|
695
681
|
}
|
|
682
|
+
isOptionalMenuTarget(target) {
|
|
683
|
+
return ["submenu", "submenuTrigger", "submenuItems"].includes(target);
|
|
684
|
+
}
|
|
696
685
|
/**
|
|
697
686
|
* Check if error is due to browser/page being closed
|
|
698
687
|
*/
|
|
@@ -813,7 +802,7 @@ var init_ActionExecutor = __esm({
|
|
|
813
802
|
} else if (keyValue.includes(" ")) {
|
|
814
803
|
keyValue = keyValue.replace(/ /g, "");
|
|
815
804
|
}
|
|
816
|
-
if (target === "focusable" && ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Escape"].includes(keyValue)) {
|
|
805
|
+
if (target === "focusable" && ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Escape", "Home", "End", "Tab", "Shift+Tab"].includes(keyValue)) {
|
|
817
806
|
await this.page.keyboard.press(keyValue);
|
|
818
807
|
return { success: true };
|
|
819
808
|
}
|
|
@@ -824,9 +813,10 @@ var init_ActionExecutor = __esm({
|
|
|
824
813
|
const locator = this.page.locator(selector).first();
|
|
825
814
|
const elementCount = await locator.count();
|
|
826
815
|
if (elementCount === 0) {
|
|
816
|
+
const optionalMenuTarget = this.isOptionalMenuTarget(target);
|
|
827
817
|
return {
|
|
828
818
|
success: false,
|
|
829
|
-
error: `${target} element not found (optional submenu test)
|
|
819
|
+
error: optionalMenuTarget ? `${target} element not found (optional submenu test)` : `${target} element not found.`,
|
|
830
820
|
shouldBreak: true
|
|
831
821
|
// Signal to skip this test
|
|
832
822
|
};
|
|
@@ -1199,23 +1189,35 @@ This usually means:
|
|
|
1199
1189
|
}).catch(() => {
|
|
1200
1190
|
});
|
|
1201
1191
|
}
|
|
1202
|
-
const
|
|
1192
|
+
const hasSubmenuCapability = componentName === "menu" && !!componentContract.selectors.submenuTrigger ? await page.locator(componentContract.selectors.submenuTrigger).count() > 0 : false;
|
|
1193
|
+
let staticFailed = 0;
|
|
1203
1194
|
const staticAssertionRunner = new AssertionRunner(page, componentContract.selectors, assertionTimeoutMs);
|
|
1204
1195
|
for (const test of componentContract.static[0]?.assertions || []) {
|
|
1205
1196
|
if (test.target === "relative") continue;
|
|
1206
1197
|
const staticDescription = `${test.target}${test.attribute ? ` (${test.attribute})` : ""}`;
|
|
1198
|
+
if (componentName === "menu" && test.target === "submenuTrigger" && !hasSubmenuCapability) {
|
|
1199
|
+
passes.push(`Skipping submenu static assertion for ${test.target}: no submenu capability detected in rendered component.`);
|
|
1200
|
+
reporter.reportStaticTest(staticDescription, true);
|
|
1201
|
+
continue;
|
|
1202
|
+
}
|
|
1207
1203
|
const targetSelector = componentContract.selectors[test.target];
|
|
1208
1204
|
if (!targetSelector) {
|
|
1209
1205
|
const failure = `Selector for target ${test.target} not found.`;
|
|
1210
1206
|
failures.push(failure);
|
|
1207
|
+
staticFailed += 1;
|
|
1211
1208
|
reporter.reportStaticTest(staticDescription, false, failure);
|
|
1212
1209
|
continue;
|
|
1213
1210
|
}
|
|
1214
1211
|
const target = page.locator(targetSelector).first();
|
|
1215
1212
|
const exists = await target.count() > 0;
|
|
1216
1213
|
if (!exists) {
|
|
1214
|
+
if (test.isOptional === true) {
|
|
1215
|
+
reporter.reportStaticTest(staticDescription, true);
|
|
1216
|
+
continue;
|
|
1217
|
+
}
|
|
1217
1218
|
const failure = `Target ${test.target} not found.`;
|
|
1218
1219
|
failures.push(failure);
|
|
1220
|
+
staticFailed += 1;
|
|
1219
1221
|
reporter.reportStaticTest(staticDescription, false, failure);
|
|
1220
1222
|
continue;
|
|
1221
1223
|
}
|
|
@@ -1252,6 +1254,7 @@ This usually means:
|
|
|
1252
1254
|
if (!hasAny && !allRedundant) {
|
|
1253
1255
|
const failure = test.failureMessage + ` None of the attributes "${test.attribute}" are present.`;
|
|
1254
1256
|
failures.push(failure);
|
|
1257
|
+
staticFailed += 1;
|
|
1255
1258
|
reporter.reportStaticTest(staticDescription, false, failure);
|
|
1256
1259
|
} else if (!allRedundant && hasAny) {
|
|
1257
1260
|
passes.push(`At least one of the attributes "${test.attribute}" exists on the element.`);
|
|
@@ -1277,6 +1280,7 @@ This usually means:
|
|
|
1277
1280
|
reporter.reportStaticTest(staticDescription, true);
|
|
1278
1281
|
} else if (!result.success && result.failMessage) {
|
|
1279
1282
|
failures.push(result.failMessage);
|
|
1283
|
+
staticFailed += 1;
|
|
1280
1284
|
reporter.reportStaticTest(staticDescription, false, result.failMessage);
|
|
1281
1285
|
}
|
|
1282
1286
|
}
|
|
@@ -1306,9 +1310,12 @@ This usually means:
|
|
|
1306
1310
|
}
|
|
1307
1311
|
const actionExecutor = new ActionExecutor(page, componentContract.selectors, actionTimeoutMs);
|
|
1308
1312
|
const assertionRunner = new AssertionRunner(page, componentContract.selectors, assertionTimeoutMs);
|
|
1313
|
+
let shouldSkipCurrentTest = false;
|
|
1314
|
+
let shouldAbortCurrentTest = false;
|
|
1309
1315
|
for (const act of action) {
|
|
1310
1316
|
if (!page || page.isClosed()) {
|
|
1311
1317
|
failures.push(`CRITICAL: Browser/page closed during test execution. Remaining actions skipped.`);
|
|
1318
|
+
shouldAbortCurrentTest = true;
|
|
1312
1319
|
break;
|
|
1313
1320
|
}
|
|
1314
1321
|
let result;
|
|
@@ -1326,18 +1333,29 @@ This usually means:
|
|
|
1326
1333
|
continue;
|
|
1327
1334
|
}
|
|
1328
1335
|
if (!result.success) {
|
|
1329
|
-
if (result.error) {
|
|
1330
|
-
failures.push(result.error);
|
|
1331
|
-
}
|
|
1332
1336
|
if (result.shouldBreak) {
|
|
1333
1337
|
if (result.error?.includes("optional submenu test")) {
|
|
1334
1338
|
reporter.reportTest(dynamicTest, "skip", result.error);
|
|
1339
|
+
shouldSkipCurrentTest = true;
|
|
1340
|
+
} else if (result.error) {
|
|
1341
|
+
failures.push(result.error);
|
|
1342
|
+
shouldAbortCurrentTest = true;
|
|
1335
1343
|
}
|
|
1336
1344
|
break;
|
|
1337
1345
|
}
|
|
1346
|
+
if (result.error) {
|
|
1347
|
+
failures.push(result.error);
|
|
1348
|
+
}
|
|
1338
1349
|
continue;
|
|
1339
1350
|
}
|
|
1340
1351
|
}
|
|
1352
|
+
if (shouldSkipCurrentTest) {
|
|
1353
|
+
continue;
|
|
1354
|
+
}
|
|
1355
|
+
if (shouldAbortCurrentTest) {
|
|
1356
|
+
reporter.reportTest(dynamicTest, "fail", failures[failures.length - 1]);
|
|
1357
|
+
continue;
|
|
1358
|
+
}
|
|
1341
1359
|
for (const assertion of assertions) {
|
|
1342
1360
|
const result = await assertionRunner.validate(assertion, dynamicTest.description);
|
|
1343
1361
|
if (result.success && result.passMessage) {
|
|
@@ -1357,7 +1375,6 @@ This usually means:
|
|
|
1357
1375
|
}
|
|
1358
1376
|
}
|
|
1359
1377
|
const staticTotal = componentContract.static[0].assertions.length;
|
|
1360
|
-
const staticFailed = failures.length - failuresBeforeStatic;
|
|
1361
1378
|
const staticPassed = Math.max(0, staticTotal - staticFailed);
|
|
1362
1379
|
reporter.reportStatic(staticPassed, staticFailed);
|
|
1363
1380
|
reporter.summary(failures);
|
|
@@ -1990,11 +2007,14 @@ function makeMenuAccessible({ menuId, menuItemsClass, triggerId, callback }) {
|
|
|
1990
2007
|
for (let i = 0; i < allItems.length; i++) {
|
|
1991
2008
|
const item = allItems.item(i);
|
|
1992
2009
|
const isNested = isItemInNestedSubmenu(item);
|
|
2010
|
+
const isDisabled = item.getAttribute("aria-disabled") === "true";
|
|
1993
2011
|
if (!isNested) {
|
|
1994
2012
|
if (!item.hasAttribute("tabindex")) {
|
|
1995
2013
|
item.setAttribute("tabindex", "-1");
|
|
1996
2014
|
}
|
|
1997
|
-
|
|
2015
|
+
if (!isDisabled) {
|
|
2016
|
+
filteredItems.push(item);
|
|
2017
|
+
}
|
|
1998
2018
|
}
|
|
1999
2019
|
}
|
|
2000
2020
|
}
|
|
@@ -2019,9 +2039,14 @@ function makeMenuAccessible({ menuId, menuItemsClass, triggerId, callback }) {
|
|
|
2019
2039
|
const items = getItems();
|
|
2020
2040
|
items.forEach((item) => {
|
|
2021
2041
|
item.setAttribute("role", "menuitem");
|
|
2022
|
-
|
|
2042
|
+
const submenuId = item.getAttribute("data-submenu-id") ?? item.getAttribute("aria-controls");
|
|
2043
|
+
const hasSubmenuTriggerAttributes = item.hasAttribute("aria-haspopup") && submenuId;
|
|
2044
|
+
if (submenuId && (item.hasAttribute("data-submenu-id") || hasSubmenuTriggerAttributes)) {
|
|
2023
2045
|
item.setAttribute("aria-haspopup", "menu");
|
|
2024
|
-
item.setAttribute("aria-controls",
|
|
2046
|
+
item.setAttribute("aria-controls", submenuId);
|
|
2047
|
+
if (!item.hasAttribute("aria-expanded")) {
|
|
2048
|
+
item.setAttribute("aria-expanded", "false");
|
|
2049
|
+
}
|
|
2025
2050
|
}
|
|
2026
2051
|
});
|
|
2027
2052
|
}
|
|
@@ -2030,24 +2055,29 @@ function makeMenuAccessible({ menuId, menuItemsClass, triggerId, callback }) {
|
|
|
2030
2055
|
const nextIndex = (currentIndex + direction + len) % len;
|
|
2031
2056
|
elementItems.item(nextIndex).focus();
|
|
2032
2057
|
}
|
|
2058
|
+
function focusItemAtIndex(items, index) {
|
|
2059
|
+
if (items.length === 0) return;
|
|
2060
|
+
items[index]?.focus();
|
|
2061
|
+
}
|
|
2033
2062
|
function hasSubmenu(menuItem) {
|
|
2034
2063
|
return menuItem.hasAttribute("aria-controls") && menuItem.hasAttribute("aria-haspopup") && menuItem.getAttribute("role") === "menuitem";
|
|
2035
2064
|
}
|
|
2036
2065
|
intializeMenuItems();
|
|
2037
2066
|
function handleItemsKeydown(event, menuItem, menuItemIndex) {
|
|
2038
2067
|
switch (event.key) {
|
|
2039
|
-
case "ArrowUp":
|
|
2040
2068
|
case "ArrowLeft": {
|
|
2041
2069
|
if (event.key === "ArrowLeft" && triggerButton.getAttribute("role") === "menuitem") {
|
|
2042
2070
|
event.preventDefault();
|
|
2043
2071
|
closeMenu();
|
|
2044
2072
|
return;
|
|
2045
2073
|
}
|
|
2074
|
+
break;
|
|
2075
|
+
}
|
|
2076
|
+
case "ArrowUp": {
|
|
2046
2077
|
event.preventDefault();
|
|
2047
2078
|
moveFocus2(toNodeListLike(getFilteredItems()), menuItemIndex, -1);
|
|
2048
2079
|
break;
|
|
2049
2080
|
}
|
|
2050
|
-
case "ArrowDown":
|
|
2051
2081
|
case "ArrowRight": {
|
|
2052
2082
|
if (event.key === "ArrowRight" && hasSubmenu(menuItem)) {
|
|
2053
2083
|
event.preventDefault();
|
|
@@ -2057,10 +2087,24 @@ function makeMenuAccessible({ menuId, menuItemsClass, triggerId, callback }) {
|
|
|
2057
2087
|
return;
|
|
2058
2088
|
}
|
|
2059
2089
|
}
|
|
2090
|
+
break;
|
|
2091
|
+
}
|
|
2092
|
+
case "ArrowDown": {
|
|
2060
2093
|
event.preventDefault();
|
|
2061
2094
|
moveFocus2(toNodeListLike(getFilteredItems()), menuItemIndex, 1);
|
|
2062
2095
|
break;
|
|
2063
2096
|
}
|
|
2097
|
+
case "Home": {
|
|
2098
|
+
event.preventDefault();
|
|
2099
|
+
focusItemAtIndex(getFilteredItems(), 0);
|
|
2100
|
+
break;
|
|
2101
|
+
}
|
|
2102
|
+
case "End": {
|
|
2103
|
+
event.preventDefault();
|
|
2104
|
+
const items = getFilteredItems();
|
|
2105
|
+
focusItemAtIndex(items, items.length - 1);
|
|
2106
|
+
break;
|
|
2107
|
+
}
|
|
2064
2108
|
case "Escape": {
|
|
2065
2109
|
event.preventDefault();
|
|
2066
2110
|
closeMenu();
|
|
@@ -2073,7 +2117,18 @@ function makeMenuAccessible({ menuId, menuItemsClass, triggerId, callback }) {
|
|
|
2073
2117
|
case "Enter":
|
|
2074
2118
|
case " ": {
|
|
2075
2119
|
event.preventDefault();
|
|
2120
|
+
if (hasSubmenu(menuItem)) {
|
|
2121
|
+
const submenuId = menuItem.getAttribute("aria-controls");
|
|
2122
|
+
if (submenuId) {
|
|
2123
|
+
openSubmenu(submenuId);
|
|
2124
|
+
return;
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2076
2127
|
menuItem.click();
|
|
2128
|
+
closeMenu();
|
|
2129
|
+
if (onOpenChange) {
|
|
2130
|
+
onOpenChange(false);
|
|
2131
|
+
}
|
|
2077
2132
|
break;
|
|
2078
2133
|
}
|
|
2079
2134
|
case "Tab": {
|
|
@@ -2182,6 +2237,7 @@ function makeMenuAccessible({ menuId, menuItemsClass, triggerId, callback }) {
|
|
|
2182
2237
|
}
|
|
2183
2238
|
}
|
|
2184
2239
|
function closeMenu() {
|
|
2240
|
+
submenuInstances.forEach((instance) => instance.closeMenu());
|
|
2185
2241
|
setAria(false);
|
|
2186
2242
|
menuDiv.style.display = "none";
|
|
2187
2243
|
removeListeners();
|
|
@@ -2213,7 +2269,6 @@ function makeMenuAccessible({ menuId, menuItemsClass, triggerId, callback }) {
|
|
|
2213
2269
|
}
|
|
2214
2270
|
triggerButton.addEventListener("click", handleTriggerClick);
|
|
2215
2271
|
document.addEventListener("click", handleClickOutside);
|
|
2216
|
-
triggerButton.setAttribute("data-menu-initialized", "true");
|
|
2217
2272
|
function cleanup() {
|
|
2218
2273
|
removeListeners();
|
|
2219
2274
|
triggerButton.removeEventListener("click", handleTriggerClick);
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
ContractReporter,
|
|
3
3
|
closeSharedBrowser,
|
|
4
4
|
contract_default
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-LKN5PRYD.js";
|
|
6
6
|
import "./chunk-I2KLQ2HA.js";
|
|
7
7
|
|
|
8
8
|
// src/accordion/src/makeAccordionAccessible/makeAccordionAccessible.ts
|
|
@@ -464,11 +464,14 @@ function makeMenuAccessible({ menuId, menuItemsClass, triggerId, callback }) {
|
|
|
464
464
|
for (let i = 0; i < allItems.length; i++) {
|
|
465
465
|
const item = allItems.item(i);
|
|
466
466
|
const isNested = isItemInNestedSubmenu(item);
|
|
467
|
+
const isDisabled = item.getAttribute("aria-disabled") === "true";
|
|
467
468
|
if (!isNested) {
|
|
468
469
|
if (!item.hasAttribute("tabindex")) {
|
|
469
470
|
item.setAttribute("tabindex", "-1");
|
|
470
471
|
}
|
|
471
|
-
|
|
472
|
+
if (!isDisabled) {
|
|
473
|
+
filteredItems.push(item);
|
|
474
|
+
}
|
|
472
475
|
}
|
|
473
476
|
}
|
|
474
477
|
}
|
|
@@ -493,9 +496,14 @@ function makeMenuAccessible({ menuId, menuItemsClass, triggerId, callback }) {
|
|
|
493
496
|
const items = getItems();
|
|
494
497
|
items.forEach((item) => {
|
|
495
498
|
item.setAttribute("role", "menuitem");
|
|
496
|
-
|
|
499
|
+
const submenuId = item.getAttribute("data-submenu-id") ?? item.getAttribute("aria-controls");
|
|
500
|
+
const hasSubmenuTriggerAttributes = item.hasAttribute("aria-haspopup") && submenuId;
|
|
501
|
+
if (submenuId && (item.hasAttribute("data-submenu-id") || hasSubmenuTriggerAttributes)) {
|
|
497
502
|
item.setAttribute("aria-haspopup", "menu");
|
|
498
|
-
item.setAttribute("aria-controls",
|
|
503
|
+
item.setAttribute("aria-controls", submenuId);
|
|
504
|
+
if (!item.hasAttribute("aria-expanded")) {
|
|
505
|
+
item.setAttribute("aria-expanded", "false");
|
|
506
|
+
}
|
|
499
507
|
}
|
|
500
508
|
});
|
|
501
509
|
}
|
|
@@ -504,24 +512,29 @@ function makeMenuAccessible({ menuId, menuItemsClass, triggerId, callback }) {
|
|
|
504
512
|
const nextIndex = (currentIndex + direction + len) % len;
|
|
505
513
|
elementItems.item(nextIndex).focus();
|
|
506
514
|
}
|
|
515
|
+
function focusItemAtIndex(items, index) {
|
|
516
|
+
if (items.length === 0) return;
|
|
517
|
+
items[index]?.focus();
|
|
518
|
+
}
|
|
507
519
|
function hasSubmenu(menuItem) {
|
|
508
520
|
return menuItem.hasAttribute("aria-controls") && menuItem.hasAttribute("aria-haspopup") && menuItem.getAttribute("role") === "menuitem";
|
|
509
521
|
}
|
|
510
522
|
intializeMenuItems();
|
|
511
523
|
function handleItemsKeydown(event, menuItem, menuItemIndex) {
|
|
512
524
|
switch (event.key) {
|
|
513
|
-
case "ArrowUp":
|
|
514
525
|
case "ArrowLeft": {
|
|
515
526
|
if (event.key === "ArrowLeft" && triggerButton.getAttribute("role") === "menuitem") {
|
|
516
527
|
event.preventDefault();
|
|
517
528
|
closeMenu();
|
|
518
529
|
return;
|
|
519
530
|
}
|
|
531
|
+
break;
|
|
532
|
+
}
|
|
533
|
+
case "ArrowUp": {
|
|
520
534
|
event.preventDefault();
|
|
521
535
|
moveFocus2(toNodeListLike(getFilteredItems()), menuItemIndex, -1);
|
|
522
536
|
break;
|
|
523
537
|
}
|
|
524
|
-
case "ArrowDown":
|
|
525
538
|
case "ArrowRight": {
|
|
526
539
|
if (event.key === "ArrowRight" && hasSubmenu(menuItem)) {
|
|
527
540
|
event.preventDefault();
|
|
@@ -531,10 +544,24 @@ function makeMenuAccessible({ menuId, menuItemsClass, triggerId, callback }) {
|
|
|
531
544
|
return;
|
|
532
545
|
}
|
|
533
546
|
}
|
|
547
|
+
break;
|
|
548
|
+
}
|
|
549
|
+
case "ArrowDown": {
|
|
534
550
|
event.preventDefault();
|
|
535
551
|
moveFocus2(toNodeListLike(getFilteredItems()), menuItemIndex, 1);
|
|
536
552
|
break;
|
|
537
553
|
}
|
|
554
|
+
case "Home": {
|
|
555
|
+
event.preventDefault();
|
|
556
|
+
focusItemAtIndex(getFilteredItems(), 0);
|
|
557
|
+
break;
|
|
558
|
+
}
|
|
559
|
+
case "End": {
|
|
560
|
+
event.preventDefault();
|
|
561
|
+
const items = getFilteredItems();
|
|
562
|
+
focusItemAtIndex(items, items.length - 1);
|
|
563
|
+
break;
|
|
564
|
+
}
|
|
538
565
|
case "Escape": {
|
|
539
566
|
event.preventDefault();
|
|
540
567
|
closeMenu();
|
|
@@ -547,7 +574,18 @@ function makeMenuAccessible({ menuId, menuItemsClass, triggerId, callback }) {
|
|
|
547
574
|
case "Enter":
|
|
548
575
|
case " ": {
|
|
549
576
|
event.preventDefault();
|
|
577
|
+
if (hasSubmenu(menuItem)) {
|
|
578
|
+
const submenuId = menuItem.getAttribute("aria-controls");
|
|
579
|
+
if (submenuId) {
|
|
580
|
+
openSubmenu(submenuId);
|
|
581
|
+
return;
|
|
582
|
+
}
|
|
583
|
+
}
|
|
550
584
|
menuItem.click();
|
|
585
|
+
closeMenu();
|
|
586
|
+
if (onOpenChange) {
|
|
587
|
+
onOpenChange(false);
|
|
588
|
+
}
|
|
551
589
|
break;
|
|
552
590
|
}
|
|
553
591
|
case "Tab": {
|
|
@@ -656,6 +694,7 @@ function makeMenuAccessible({ menuId, menuItemsClass, triggerId, callback }) {
|
|
|
656
694
|
}
|
|
657
695
|
}
|
|
658
696
|
function closeMenu() {
|
|
697
|
+
submenuInstances.forEach((instance) => instance.closeMenu());
|
|
659
698
|
setAria(false);
|
|
660
699
|
menuDiv.style.display = "none";
|
|
661
700
|
removeListeners();
|
|
@@ -687,7 +726,6 @@ function makeMenuAccessible({ menuId, menuItemsClass, triggerId, callback }) {
|
|
|
687
726
|
}
|
|
688
727
|
triggerButton.addEventListener("click", handleTriggerClick);
|
|
689
728
|
document.addEventListener("click", handleClickOutside);
|
|
690
|
-
triggerButton.setAttribute("data-menu-initialized", "true");
|
|
691
729
|
function cleanup() {
|
|
692
730
|
removeListeners();
|
|
693
731
|
triggerButton.removeEventListener("click", handleTriggerClick);
|
|
@@ -1551,7 +1589,7 @@ Error: ${error instanceof Error ? error.message : String(error)}`
|
|
|
1551
1589
|
const devServerUrl = await checkDevServer(url);
|
|
1552
1590
|
if (devServerUrl) {
|
|
1553
1591
|
console.log(`\u{1F3AD} Running Playwright tests on ${devServerUrl}`);
|
|
1554
|
-
const { runContractTestsPlaywright } = await import("./contractTestRunnerPlaywright-
|
|
1592
|
+
const { runContractTestsPlaywright } = await import("./contractTestRunnerPlaywright-PC6JOYYV.js");
|
|
1555
1593
|
contract = await runContractTestsPlaywright(componentName, devServerUrl);
|
|
1556
1594
|
} else {
|
|
1557
1595
|
throw new Error(
|