aria-ease 3.0.2 → 4.0.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 +6 -6
- package/bin/cli.cjs +90 -22
- package/bin/cli.js +1 -1
- package/bin/{contractTestRunnerPlaywright-TLQZGKW7.js → contractTestRunnerPlaywright-EZLNNJV5.js} +43 -10
- package/bin/{test-Q7W3WQEA.js → test-45KMD4F4.js} +47 -12
- package/dist/{contractTestRunnerPlaywright-7U2O33SR.js → contractTestRunnerPlaywright-UQQI5MYS.js} +43 -10
- package/dist/index.cjs +663 -38
- package/dist/index.d.cts +88 -6
- package/dist/index.d.ts +88 -6
- package/dist/index.js +616 -28
- package/dist/src/{Types.d-uG0Hm1yK.d.ts → Types.d-BrHSyS03.d.cts} +17 -0
- package/dist/src/{Types.d-uG0Hm1yK.d.cts → Types.d-BrHSyS03.d.ts} +17 -0
- package/dist/src/accordion/index.cjs +159 -0
- package/dist/src/accordion/index.d.cts +19 -2
- package/dist/src/accordion/index.d.ts +19 -2
- package/dist/src/accordion/index.js +159 -1
- package/dist/src/block/index.cjs +1 -1
- package/dist/src/block/index.d.cts +6 -2
- package/dist/src/block/index.d.ts +6 -2
- package/dist/src/block/index.js +1 -1
- package/dist/src/checkbox/index.cjs +129 -0
- package/dist/src/checkbox/index.d.cts +15 -2
- package/dist/src/checkbox/index.d.ts +15 -2
- package/dist/src/checkbox/index.js +129 -1
- package/dist/src/combobox/index.d.cts +1 -1
- package/dist/src/combobox/index.d.ts +1 -1
- package/dist/src/menu/index.cjs +13 -15
- package/dist/src/menu/index.d.cts +1 -1
- package/dist/src/menu/index.d.ts +1 -1
- package/dist/src/menu/index.js +13 -15
- package/dist/src/radio/index.cjs +122 -0
- package/dist/src/radio/index.d.cts +17 -2
- package/dist/src/radio/index.d.ts +17 -2
- package/dist/src/radio/index.js +122 -1
- package/dist/src/toggle/index.cjs +145 -0
- package/dist/src/toggle/index.d.cts +17 -2
- package/dist/src/toggle/index.d.ts +17 -2
- package/dist/src/toggle/index.js +145 -1
- package/dist/src/utils/test/{contractTestRunnerPlaywright-7U2O33SR.js → contractTestRunnerPlaywright-UQQI5MYS.js} +43 -10
- package/dist/src/utils/test/index.cjs +90 -22
- package/dist/src/utils/test/index.d.cts +5 -4
- package/dist/src/utils/test/index.d.ts +5 -4
- package/dist/src/utils/test/index.js +47 -12
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -9208,6 +9208,164 @@ function updateAccordionTriggerAriaAttributes(accordionId, accordionTriggersClas
|
|
|
9208
9208
|
});
|
|
9209
9209
|
}
|
|
9210
9210
|
|
|
9211
|
+
// src/accordion/src/makeAccordionAccessible/makeAccordionAccessible.ts
|
|
9212
|
+
function makeAccordionAccessible({ accordionId, triggersClass, panelsClass, allowMultiple = false }) {
|
|
9213
|
+
const accordionContainer = document.querySelector(`#${accordionId}`);
|
|
9214
|
+
if (!accordionContainer) {
|
|
9215
|
+
console.error(`[aria-ease] Element with id="${accordionId}" not found. Make sure the accordion container exists before calling makeAccordionAccessible.`);
|
|
9216
|
+
return { cleanup: () => {
|
|
9217
|
+
} };
|
|
9218
|
+
}
|
|
9219
|
+
const triggers = Array.from(accordionContainer.querySelectorAll(`.${triggersClass}`));
|
|
9220
|
+
if (triggers.length === 0) {
|
|
9221
|
+
console.error(`[aria-ease] No elements with class="${triggersClass}" found. Make sure accordion triggers exist before calling makeAccordionAccessible.`);
|
|
9222
|
+
return { cleanup: () => {
|
|
9223
|
+
} };
|
|
9224
|
+
}
|
|
9225
|
+
const panels = Array.from(accordionContainer.querySelectorAll(`.${panelsClass}`));
|
|
9226
|
+
if (panels.length === 0) {
|
|
9227
|
+
console.error(`[aria-ease] No elements with class="${panelsClass}" found. Make sure accordion panels exist before calling makeAccordionAccessible.`);
|
|
9228
|
+
return { cleanup: () => {
|
|
9229
|
+
} };
|
|
9230
|
+
}
|
|
9231
|
+
if (triggers.length !== panels.length) {
|
|
9232
|
+
console.error(`[aria-ease] Accordion trigger/panel mismatch: found ${triggers.length} triggers but ${panels.length} panels.`);
|
|
9233
|
+
return { cleanup: () => {
|
|
9234
|
+
} };
|
|
9235
|
+
}
|
|
9236
|
+
const handlerMap = /* @__PURE__ */ new WeakMap();
|
|
9237
|
+
const clickHandlerMap = /* @__PURE__ */ new WeakMap();
|
|
9238
|
+
function initialize() {
|
|
9239
|
+
triggers.forEach((trigger, index) => {
|
|
9240
|
+
const panel = panels[index];
|
|
9241
|
+
if (!trigger.id) {
|
|
9242
|
+
trigger.id = `${accordionId}-trigger-${index}`;
|
|
9243
|
+
}
|
|
9244
|
+
if (!panel.id) {
|
|
9245
|
+
panel.id = `${accordionId}-panel-${index}`;
|
|
9246
|
+
}
|
|
9247
|
+
trigger.setAttribute("aria-controls", panel.id);
|
|
9248
|
+
trigger.setAttribute("aria-expanded", "false");
|
|
9249
|
+
panel.setAttribute("role", "region");
|
|
9250
|
+
panel.setAttribute("aria-labelledby", trigger.id);
|
|
9251
|
+
panel.style.display = "none";
|
|
9252
|
+
});
|
|
9253
|
+
}
|
|
9254
|
+
function expandItem(index) {
|
|
9255
|
+
if (index < 0 || index >= triggers.length) {
|
|
9256
|
+
console.error(`[aria-ease] Invalid accordion index: ${index}`);
|
|
9257
|
+
return;
|
|
9258
|
+
}
|
|
9259
|
+
const trigger = triggers[index];
|
|
9260
|
+
const panel = panels[index];
|
|
9261
|
+
trigger.setAttribute("aria-expanded", "true");
|
|
9262
|
+
panel.style.display = "block";
|
|
9263
|
+
}
|
|
9264
|
+
function collapseItem(index) {
|
|
9265
|
+
if (index < 0 || index >= triggers.length) {
|
|
9266
|
+
console.error(`[aria-ease] Invalid accordion index: ${index}`);
|
|
9267
|
+
return;
|
|
9268
|
+
}
|
|
9269
|
+
const trigger = triggers[index];
|
|
9270
|
+
const panel = panels[index];
|
|
9271
|
+
trigger.setAttribute("aria-expanded", "false");
|
|
9272
|
+
panel.style.display = "none";
|
|
9273
|
+
}
|
|
9274
|
+
function toggleItem(index) {
|
|
9275
|
+
const trigger = triggers[index];
|
|
9276
|
+
const isExpanded = trigger.getAttribute("aria-expanded") === "true";
|
|
9277
|
+
if (isExpanded) {
|
|
9278
|
+
collapseItem(index);
|
|
9279
|
+
} else {
|
|
9280
|
+
if (!allowMultiple) {
|
|
9281
|
+
triggers.forEach((_, i) => {
|
|
9282
|
+
if (i !== index) {
|
|
9283
|
+
collapseItem(i);
|
|
9284
|
+
}
|
|
9285
|
+
});
|
|
9286
|
+
}
|
|
9287
|
+
expandItem(index);
|
|
9288
|
+
}
|
|
9289
|
+
}
|
|
9290
|
+
function handleTriggerClick(index) {
|
|
9291
|
+
return () => {
|
|
9292
|
+
toggleItem(index);
|
|
9293
|
+
};
|
|
9294
|
+
}
|
|
9295
|
+
function handleTriggerKeydown(index) {
|
|
9296
|
+
return (event) => {
|
|
9297
|
+
const { key } = event;
|
|
9298
|
+
switch (key) {
|
|
9299
|
+
case "Enter":
|
|
9300
|
+
case " ":
|
|
9301
|
+
event.preventDefault();
|
|
9302
|
+
toggleItem(index);
|
|
9303
|
+
break;
|
|
9304
|
+
case "ArrowDown":
|
|
9305
|
+
event.preventDefault();
|
|
9306
|
+
{
|
|
9307
|
+
const nextIndex = (index + 1) % triggers.length;
|
|
9308
|
+
triggers[nextIndex].focus();
|
|
9309
|
+
}
|
|
9310
|
+
break;
|
|
9311
|
+
case "ArrowUp":
|
|
9312
|
+
event.preventDefault();
|
|
9313
|
+
{
|
|
9314
|
+
const prevIndex = (index - 1 + triggers.length) % triggers.length;
|
|
9315
|
+
triggers[prevIndex].focus();
|
|
9316
|
+
}
|
|
9317
|
+
break;
|
|
9318
|
+
case "Home":
|
|
9319
|
+
event.preventDefault();
|
|
9320
|
+
triggers[0].focus();
|
|
9321
|
+
break;
|
|
9322
|
+
case "End":
|
|
9323
|
+
event.preventDefault();
|
|
9324
|
+
triggers[triggers.length - 1].focus();
|
|
9325
|
+
break;
|
|
9326
|
+
}
|
|
9327
|
+
};
|
|
9328
|
+
}
|
|
9329
|
+
function addListeners() {
|
|
9330
|
+
triggers.forEach((trigger, index) => {
|
|
9331
|
+
const clickHandler = handleTriggerClick(index);
|
|
9332
|
+
const keydownHandler = handleTriggerKeydown(index);
|
|
9333
|
+
trigger.addEventListener("click", clickHandler);
|
|
9334
|
+
trigger.addEventListener("keydown", keydownHandler);
|
|
9335
|
+
handlerMap.set(trigger, keydownHandler);
|
|
9336
|
+
clickHandlerMap.set(trigger, clickHandler);
|
|
9337
|
+
});
|
|
9338
|
+
}
|
|
9339
|
+
function removeListeners() {
|
|
9340
|
+
triggers.forEach((trigger) => {
|
|
9341
|
+
const keydownHandler = handlerMap.get(trigger);
|
|
9342
|
+
const clickHandler = clickHandlerMap.get(trigger);
|
|
9343
|
+
if (keydownHandler) {
|
|
9344
|
+
trigger.removeEventListener("keydown", keydownHandler);
|
|
9345
|
+
handlerMap.delete(trigger);
|
|
9346
|
+
}
|
|
9347
|
+
if (clickHandler) {
|
|
9348
|
+
trigger.removeEventListener("click", clickHandler);
|
|
9349
|
+
clickHandlerMap.delete(trigger);
|
|
9350
|
+
}
|
|
9351
|
+
});
|
|
9352
|
+
}
|
|
9353
|
+
function cleanup() {
|
|
9354
|
+
removeListeners();
|
|
9355
|
+
triggers.forEach((_, index) => {
|
|
9356
|
+
collapseItem(index);
|
|
9357
|
+
});
|
|
9358
|
+
}
|
|
9359
|
+
initialize();
|
|
9360
|
+
addListeners();
|
|
9361
|
+
return {
|
|
9362
|
+
expandItem,
|
|
9363
|
+
collapseItem,
|
|
9364
|
+
toggleItem,
|
|
9365
|
+
cleanup
|
|
9366
|
+
};
|
|
9367
|
+
}
|
|
9368
|
+
|
|
9211
9369
|
// src/utils/handleKeyPress/handleKeyPress.ts
|
|
9212
9370
|
function isTextInput(el) {
|
|
9213
9371
|
if (el.tagName !== "INPUT") return false;
|
|
@@ -9331,7 +9489,7 @@ function handleKeyPress(event, elementItems, elementItemIndex, menuElementDiv, t
|
|
|
9331
9489
|
}
|
|
9332
9490
|
|
|
9333
9491
|
// src/block/src/makeBlockAccessible/makeBlockAccessible.ts
|
|
9334
|
-
function makeBlockAccessible(blockId, blockItemsClass) {
|
|
9492
|
+
function makeBlockAccessible({ blockId, blockItemsClass }) {
|
|
9335
9493
|
const blockDiv = document.querySelector(`#${blockId}`);
|
|
9336
9494
|
if (!blockDiv) {
|
|
9337
9495
|
console.error(`[aria-ease] Element with id="${blockId}" not found. Make sure the block element exists before calling makeBlockAccessible.`);
|
|
@@ -9410,6 +9568,134 @@ function updateCheckboxAriaAttributes(checkboxId, checkboxesClass, checkboxState
|
|
|
9410
9568
|
});
|
|
9411
9569
|
}
|
|
9412
9570
|
|
|
9571
|
+
// src/checkbox/src/makeCheckboxAccessible/makeCheckboxAccessible.ts
|
|
9572
|
+
function makeCheckboxAccessible({ checkboxGroupId, checkboxesClass }) {
|
|
9573
|
+
const checkboxGroup = document.querySelector(`#${checkboxGroupId}`);
|
|
9574
|
+
if (!checkboxGroup) {
|
|
9575
|
+
console.error(`[aria-ease] Element with id="${checkboxGroupId}" not found. Make sure the checkbox group container exists before calling makeCheckboxAccessible.`);
|
|
9576
|
+
return { cleanup: () => {
|
|
9577
|
+
} };
|
|
9578
|
+
}
|
|
9579
|
+
const checkboxes = Array.from(checkboxGroup.querySelectorAll(`.${checkboxesClass}`));
|
|
9580
|
+
if (checkboxes.length === 0) {
|
|
9581
|
+
console.error(`[aria-ease] No elements with class="${checkboxesClass}" found. Make sure checkboxes exist before calling makeCheckboxAccessible.`);
|
|
9582
|
+
return { cleanup: () => {
|
|
9583
|
+
} };
|
|
9584
|
+
}
|
|
9585
|
+
const handlerMap = /* @__PURE__ */ new WeakMap();
|
|
9586
|
+
const clickHandlerMap = /* @__PURE__ */ new WeakMap();
|
|
9587
|
+
function initialize() {
|
|
9588
|
+
if (!checkboxGroup.getAttribute("role")) {
|
|
9589
|
+
checkboxGroup.setAttribute("role", "group");
|
|
9590
|
+
}
|
|
9591
|
+
checkboxes.forEach((checkbox) => {
|
|
9592
|
+
checkbox.setAttribute("role", "checkbox");
|
|
9593
|
+
if (!checkbox.hasAttribute("aria-checked")) {
|
|
9594
|
+
checkbox.setAttribute("aria-checked", "false");
|
|
9595
|
+
}
|
|
9596
|
+
if (!checkbox.hasAttribute("tabindex")) {
|
|
9597
|
+
checkbox.setAttribute("tabindex", "0");
|
|
9598
|
+
}
|
|
9599
|
+
});
|
|
9600
|
+
}
|
|
9601
|
+
function toggleCheckbox(index) {
|
|
9602
|
+
if (index < 0 || index >= checkboxes.length) {
|
|
9603
|
+
console.error(`[aria-ease] Invalid checkbox index: ${index}`);
|
|
9604
|
+
return;
|
|
9605
|
+
}
|
|
9606
|
+
const checkbox = checkboxes[index];
|
|
9607
|
+
const isChecked = checkbox.getAttribute("aria-checked") === "true";
|
|
9608
|
+
checkbox.setAttribute("aria-checked", isChecked ? "false" : "true");
|
|
9609
|
+
}
|
|
9610
|
+
function setCheckboxState(index, checked) {
|
|
9611
|
+
if (index < 0 || index >= checkboxes.length) {
|
|
9612
|
+
console.error(`[aria-ease] Invalid checkbox index: ${index}`);
|
|
9613
|
+
return;
|
|
9614
|
+
}
|
|
9615
|
+
checkboxes[index].setAttribute("aria-checked", checked ? "true" : "false");
|
|
9616
|
+
}
|
|
9617
|
+
function handleCheckboxClick(index) {
|
|
9618
|
+
return () => {
|
|
9619
|
+
toggleCheckbox(index);
|
|
9620
|
+
};
|
|
9621
|
+
}
|
|
9622
|
+
function handleCheckboxKeydown(index) {
|
|
9623
|
+
return (event) => {
|
|
9624
|
+
const { key } = event;
|
|
9625
|
+
switch (key) {
|
|
9626
|
+
case " ":
|
|
9627
|
+
event.preventDefault();
|
|
9628
|
+
toggleCheckbox(index);
|
|
9629
|
+
break;
|
|
9630
|
+
case "ArrowDown":
|
|
9631
|
+
event.preventDefault();
|
|
9632
|
+
{
|
|
9633
|
+
const nextIndex = (index + 1) % checkboxes.length;
|
|
9634
|
+
checkboxes[nextIndex].focus();
|
|
9635
|
+
}
|
|
9636
|
+
break;
|
|
9637
|
+
case "ArrowUp":
|
|
9638
|
+
event.preventDefault();
|
|
9639
|
+
{
|
|
9640
|
+
const prevIndex = (index - 1 + checkboxes.length) % checkboxes.length;
|
|
9641
|
+
checkboxes[prevIndex].focus();
|
|
9642
|
+
}
|
|
9643
|
+
break;
|
|
9644
|
+
case "Home":
|
|
9645
|
+
event.preventDefault();
|
|
9646
|
+
checkboxes[0].focus();
|
|
9647
|
+
break;
|
|
9648
|
+
case "End":
|
|
9649
|
+
event.preventDefault();
|
|
9650
|
+
checkboxes[checkboxes.length - 1].focus();
|
|
9651
|
+
break;
|
|
9652
|
+
}
|
|
9653
|
+
};
|
|
9654
|
+
}
|
|
9655
|
+
function addListeners() {
|
|
9656
|
+
checkboxes.forEach((checkbox, index) => {
|
|
9657
|
+
const clickHandler = handleCheckboxClick(index);
|
|
9658
|
+
const keydownHandler = handleCheckboxKeydown(index);
|
|
9659
|
+
checkbox.addEventListener("click", clickHandler);
|
|
9660
|
+
checkbox.addEventListener("keydown", keydownHandler);
|
|
9661
|
+
handlerMap.set(checkbox, keydownHandler);
|
|
9662
|
+
clickHandlerMap.set(checkbox, clickHandler);
|
|
9663
|
+
});
|
|
9664
|
+
}
|
|
9665
|
+
function removeListeners() {
|
|
9666
|
+
checkboxes.forEach((checkbox) => {
|
|
9667
|
+
const keydownHandler = handlerMap.get(checkbox);
|
|
9668
|
+
const clickHandler = clickHandlerMap.get(checkbox);
|
|
9669
|
+
if (keydownHandler) {
|
|
9670
|
+
checkbox.removeEventListener("keydown", keydownHandler);
|
|
9671
|
+
handlerMap.delete(checkbox);
|
|
9672
|
+
}
|
|
9673
|
+
if (clickHandler) {
|
|
9674
|
+
checkbox.removeEventListener("click", clickHandler);
|
|
9675
|
+
clickHandlerMap.delete(checkbox);
|
|
9676
|
+
}
|
|
9677
|
+
});
|
|
9678
|
+
}
|
|
9679
|
+
function cleanup() {
|
|
9680
|
+
removeListeners();
|
|
9681
|
+
}
|
|
9682
|
+
function getCheckedStates() {
|
|
9683
|
+
return checkboxes.map((checkbox) => checkbox.getAttribute("aria-checked") === "true");
|
|
9684
|
+
}
|
|
9685
|
+
function getCheckedIndices() {
|
|
9686
|
+
return checkboxes.map((checkbox, index) => checkbox.getAttribute("aria-checked") === "true" ? index : -1).filter((index) => index !== -1);
|
|
9687
|
+
}
|
|
9688
|
+
initialize();
|
|
9689
|
+
addListeners();
|
|
9690
|
+
return {
|
|
9691
|
+
toggleCheckbox,
|
|
9692
|
+
setCheckboxState,
|
|
9693
|
+
getCheckedStates,
|
|
9694
|
+
getCheckedIndices,
|
|
9695
|
+
cleanup
|
|
9696
|
+
};
|
|
9697
|
+
}
|
|
9698
|
+
|
|
9413
9699
|
// src/menu/src/makeMenuAccessible/makeMenuAccessible.ts
|
|
9414
9700
|
function makeMenuAccessible({ menuId, menuItemsClass, triggerId }) {
|
|
9415
9701
|
const menuDiv = document.querySelector(`#${menuId}`);
|
|
@@ -9440,7 +9726,6 @@ function makeMenuAccessible({ menuId, menuItemsClass, triggerId }) {
|
|
|
9440
9726
|
triggerButton.setAttribute("aria-expanded", "false");
|
|
9441
9727
|
menuDiv.setAttribute("role", "menu");
|
|
9442
9728
|
menuDiv.setAttribute("aria-labelledby", triggerId);
|
|
9443
|
-
menuDiv.style.display = "none";
|
|
9444
9729
|
const handlerMap = /* @__PURE__ */ new WeakMap();
|
|
9445
9730
|
const submenuInstances = /* @__PURE__ */ new Map();
|
|
9446
9731
|
let cachedItems = null;
|
|
@@ -9576,41 +9861,40 @@ function makeMenuAccessible({ menuId, menuItemsClass, triggerId }) {
|
|
|
9576
9861
|
item.setAttribute("role", "menuitem");
|
|
9577
9862
|
});
|
|
9578
9863
|
}
|
|
9864
|
+
intializeMenuItems();
|
|
9579
9865
|
function handleTriggerKeydown(event) {
|
|
9580
|
-
|
|
9581
|
-
if (key === "Enter" || key === " ") {
|
|
9866
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
9582
9867
|
event.preventDefault();
|
|
9583
|
-
const
|
|
9584
|
-
if (
|
|
9868
|
+
const isOpen = menuDiv.style.display !== "none";
|
|
9869
|
+
if (isOpen) {
|
|
9585
9870
|
closeMenu();
|
|
9586
9871
|
} else {
|
|
9587
9872
|
openMenu();
|
|
9588
9873
|
}
|
|
9589
9874
|
}
|
|
9590
9875
|
}
|
|
9591
|
-
let isTransitioning = false;
|
|
9592
9876
|
function handleTriggerClick() {
|
|
9593
|
-
|
|
9594
|
-
|
|
9595
|
-
isTransitioning = true;
|
|
9596
|
-
if (isExpanded) {
|
|
9877
|
+
const isOpen = menuDiv.style.display !== "none";
|
|
9878
|
+
if (isOpen) {
|
|
9597
9879
|
closeMenu();
|
|
9598
9880
|
} else {
|
|
9599
9881
|
openMenu();
|
|
9600
9882
|
}
|
|
9601
|
-
setTimeout(() => {
|
|
9602
|
-
isTransitioning = false;
|
|
9603
|
-
}, 100);
|
|
9604
9883
|
}
|
|
9605
|
-
|
|
9884
|
+
function handleClickOutside(event) {
|
|
9885
|
+
if (menuDiv && triggerButton && !menuDiv.contains(event.target) && !triggerButton.contains(event.target) && getComputedStyle(menuDiv).display !== "none" && triggerButton.getAttribute("aria-expanded") === "true") {
|
|
9886
|
+
closeMenu();
|
|
9887
|
+
}
|
|
9888
|
+
}
|
|
9606
9889
|
triggerButton.addEventListener("keydown", handleTriggerKeydown);
|
|
9607
9890
|
triggerButton.addEventListener("click", handleTriggerClick);
|
|
9891
|
+
document.addEventListener("click", handleClickOutside);
|
|
9608
9892
|
triggerButton.setAttribute("data-menu-initialized", "true");
|
|
9609
9893
|
function cleanup() {
|
|
9610
9894
|
removeListeners();
|
|
9611
9895
|
triggerButton.removeEventListener("keydown", handleTriggerKeydown);
|
|
9612
9896
|
triggerButton.removeEventListener("click", handleTriggerClick);
|
|
9613
|
-
|
|
9897
|
+
document.removeEventListener("click", handleClickOutside);
|
|
9614
9898
|
menuDiv.style.display = "none";
|
|
9615
9899
|
setAria(false);
|
|
9616
9900
|
submenuInstances.forEach((instance) => instance.cleanup());
|
|
@@ -9653,6 +9937,127 @@ function updateRadioAriaAttributes(radioId, radiosClass, radioStates, currentPre
|
|
|
9653
9937
|
});
|
|
9654
9938
|
}
|
|
9655
9939
|
|
|
9940
|
+
// src/radio/src/makeRadioAccessible/makeRadioAccessible.ts
|
|
9941
|
+
function makeRadioAccessible({ radioGroupId, radiosClass, defaultSelectedIndex = 0 }) {
|
|
9942
|
+
const radioGroup = document.querySelector(`#${radioGroupId}`);
|
|
9943
|
+
if (!radioGroup) {
|
|
9944
|
+
console.error(`[aria-ease] Element with id="${radioGroupId}" not found. Make sure the radio group container exists before calling makeRadioAccessible.`);
|
|
9945
|
+
return { cleanup: () => {
|
|
9946
|
+
} };
|
|
9947
|
+
}
|
|
9948
|
+
const radios = Array.from(radioGroup.querySelectorAll(`.${radiosClass}`));
|
|
9949
|
+
if (radios.length === 0) {
|
|
9950
|
+
console.error(`[aria-ease] No elements with class="${radiosClass}" found. Make sure radio buttons exist before calling makeRadioAccessible.`);
|
|
9951
|
+
return { cleanup: () => {
|
|
9952
|
+
} };
|
|
9953
|
+
}
|
|
9954
|
+
const handlerMap = /* @__PURE__ */ new WeakMap();
|
|
9955
|
+
const clickHandlerMap = /* @__PURE__ */ new WeakMap();
|
|
9956
|
+
let currentSelectedIndex = defaultSelectedIndex;
|
|
9957
|
+
function initialize() {
|
|
9958
|
+
if (!radioGroup.getAttribute("role")) {
|
|
9959
|
+
radioGroup.setAttribute("role", "radiogroup");
|
|
9960
|
+
}
|
|
9961
|
+
radios.forEach((radio, index) => {
|
|
9962
|
+
radio.setAttribute("role", "radio");
|
|
9963
|
+
radio.setAttribute("tabindex", index === currentSelectedIndex ? "0" : "-1");
|
|
9964
|
+
if (index === currentSelectedIndex) {
|
|
9965
|
+
radio.setAttribute("aria-checked", "true");
|
|
9966
|
+
} else {
|
|
9967
|
+
radio.setAttribute("aria-checked", "false");
|
|
9968
|
+
}
|
|
9969
|
+
});
|
|
9970
|
+
}
|
|
9971
|
+
function selectRadio(index) {
|
|
9972
|
+
if (index < 0 || index >= radios.length) {
|
|
9973
|
+
console.error(`[aria-ease] Invalid radio index: ${index}`);
|
|
9974
|
+
return;
|
|
9975
|
+
}
|
|
9976
|
+
if (currentSelectedIndex >= 0 && currentSelectedIndex < radios.length) {
|
|
9977
|
+
radios[currentSelectedIndex].setAttribute("aria-checked", "false");
|
|
9978
|
+
radios[currentSelectedIndex].setAttribute("tabindex", "-1");
|
|
9979
|
+
}
|
|
9980
|
+
radios[index].setAttribute("aria-checked", "true");
|
|
9981
|
+
radios[index].setAttribute("tabindex", "0");
|
|
9982
|
+
radios[index].focus();
|
|
9983
|
+
currentSelectedIndex = index;
|
|
9984
|
+
}
|
|
9985
|
+
function handleRadioClick(index) {
|
|
9986
|
+
return () => {
|
|
9987
|
+
selectRadio(index);
|
|
9988
|
+
};
|
|
9989
|
+
}
|
|
9990
|
+
function handleRadioKeydown(index) {
|
|
9991
|
+
return (event) => {
|
|
9992
|
+
const { key } = event;
|
|
9993
|
+
let nextIndex = index;
|
|
9994
|
+
switch (key) {
|
|
9995
|
+
case "ArrowDown":
|
|
9996
|
+
case "ArrowRight":
|
|
9997
|
+
event.preventDefault();
|
|
9998
|
+
nextIndex = (index + 1) % radios.length;
|
|
9999
|
+
selectRadio(nextIndex);
|
|
10000
|
+
break;
|
|
10001
|
+
case "ArrowUp":
|
|
10002
|
+
case "ArrowLeft":
|
|
10003
|
+
event.preventDefault();
|
|
10004
|
+
nextIndex = (index - 1 + radios.length) % radios.length;
|
|
10005
|
+
selectRadio(nextIndex);
|
|
10006
|
+
break;
|
|
10007
|
+
case " ":
|
|
10008
|
+
event.preventDefault();
|
|
10009
|
+
selectRadio(index);
|
|
10010
|
+
break;
|
|
10011
|
+
case "Home":
|
|
10012
|
+
event.preventDefault();
|
|
10013
|
+
selectRadio(0);
|
|
10014
|
+
break;
|
|
10015
|
+
case "End":
|
|
10016
|
+
event.preventDefault();
|
|
10017
|
+
selectRadio(radios.length - 1);
|
|
10018
|
+
break;
|
|
10019
|
+
}
|
|
10020
|
+
};
|
|
10021
|
+
}
|
|
10022
|
+
function addListeners() {
|
|
10023
|
+
radios.forEach((radio, index) => {
|
|
10024
|
+
const clickHandler = handleRadioClick(index);
|
|
10025
|
+
const keydownHandler = handleRadioKeydown(index);
|
|
10026
|
+
radio.addEventListener("click", clickHandler);
|
|
10027
|
+
radio.addEventListener("keydown", keydownHandler);
|
|
10028
|
+
handlerMap.set(radio, keydownHandler);
|
|
10029
|
+
clickHandlerMap.set(radio, clickHandler);
|
|
10030
|
+
});
|
|
10031
|
+
}
|
|
10032
|
+
function removeListeners() {
|
|
10033
|
+
radios.forEach((radio) => {
|
|
10034
|
+
const keydownHandler = handlerMap.get(radio);
|
|
10035
|
+
const clickHandler = clickHandlerMap.get(radio);
|
|
10036
|
+
if (keydownHandler) {
|
|
10037
|
+
radio.removeEventListener("keydown", keydownHandler);
|
|
10038
|
+
handlerMap.delete(radio);
|
|
10039
|
+
}
|
|
10040
|
+
if (clickHandler) {
|
|
10041
|
+
radio.removeEventListener("click", clickHandler);
|
|
10042
|
+
clickHandlerMap.delete(radio);
|
|
10043
|
+
}
|
|
10044
|
+
});
|
|
10045
|
+
}
|
|
10046
|
+
function cleanup() {
|
|
10047
|
+
removeListeners();
|
|
10048
|
+
}
|
|
10049
|
+
function getSelectedIndex() {
|
|
10050
|
+
return currentSelectedIndex;
|
|
10051
|
+
}
|
|
10052
|
+
initialize();
|
|
10053
|
+
addListeners();
|
|
10054
|
+
return {
|
|
10055
|
+
selectRadio,
|
|
10056
|
+
getSelectedIndex,
|
|
10057
|
+
cleanup
|
|
10058
|
+
};
|
|
10059
|
+
}
|
|
10060
|
+
|
|
9656
10061
|
// src/toggle/src/updateToggleAriaAttribute/updateToggleAriaAttribute.ts
|
|
9657
10062
|
function updateToggleAriaAttribute(toggleId, togglesClass, toggleStates, currentPressedToggleIndex) {
|
|
9658
10063
|
const toggleDiv = document.querySelector(`#${toggleId}`);
|
|
@@ -9680,6 +10085,150 @@ function updateToggleAriaAttribute(toggleId, togglesClass, toggleStates, current
|
|
|
9680
10085
|
});
|
|
9681
10086
|
}
|
|
9682
10087
|
|
|
10088
|
+
// src/toggle/src/makeTogggleAccessible/makeToggleAccessible.ts
|
|
10089
|
+
function makeToggleAccessible({ toggleId, togglesClass, isSingleToggle = true }) {
|
|
10090
|
+
const toggleContainer = document.querySelector(`#${toggleId}`);
|
|
10091
|
+
if (!toggleContainer) {
|
|
10092
|
+
console.error(`[aria-ease] Element with id="${toggleId}" not found. Make sure the toggle element exists before calling makeToggleAccessible.`);
|
|
10093
|
+
return { cleanup: () => {
|
|
10094
|
+
} };
|
|
10095
|
+
}
|
|
10096
|
+
let toggles;
|
|
10097
|
+
if (isSingleToggle) {
|
|
10098
|
+
toggles = [toggleContainer];
|
|
10099
|
+
} else {
|
|
10100
|
+
if (!togglesClass) {
|
|
10101
|
+
console.error(`[aria-ease] togglesClass is required when isSingleToggle is false.`);
|
|
10102
|
+
return { cleanup: () => {
|
|
10103
|
+
} };
|
|
10104
|
+
}
|
|
10105
|
+
toggles = Array.from(toggleContainer.querySelectorAll(`.${togglesClass}`));
|
|
10106
|
+
if (toggles.length === 0) {
|
|
10107
|
+
console.error(`[aria-ease] No elements with class="${togglesClass}" found. Make sure toggle buttons exist before calling makeToggleAccessible.`);
|
|
10108
|
+
return { cleanup: () => {
|
|
10109
|
+
} };
|
|
10110
|
+
}
|
|
10111
|
+
}
|
|
10112
|
+
const handlerMap = /* @__PURE__ */ new WeakMap();
|
|
10113
|
+
const clickHandlerMap = /* @__PURE__ */ new WeakMap();
|
|
10114
|
+
function initialize() {
|
|
10115
|
+
toggles.forEach((toggle) => {
|
|
10116
|
+
if (toggle.tagName.toLowerCase() !== "button" && !toggle.getAttribute("role")) {
|
|
10117
|
+
toggle.setAttribute("role", "button");
|
|
10118
|
+
}
|
|
10119
|
+
if (!toggle.hasAttribute("aria-pressed")) {
|
|
10120
|
+
toggle.setAttribute("aria-pressed", "false");
|
|
10121
|
+
}
|
|
10122
|
+
if (!toggle.hasAttribute("tabindex")) {
|
|
10123
|
+
toggle.setAttribute("tabindex", "0");
|
|
10124
|
+
}
|
|
10125
|
+
});
|
|
10126
|
+
}
|
|
10127
|
+
function toggleButton(index) {
|
|
10128
|
+
if (index < 0 || index >= toggles.length) {
|
|
10129
|
+
console.error(`[aria-ease] Invalid toggle index: ${index}`);
|
|
10130
|
+
return;
|
|
10131
|
+
}
|
|
10132
|
+
const toggle = toggles[index];
|
|
10133
|
+
const isPressed = toggle.getAttribute("aria-pressed") === "true";
|
|
10134
|
+
toggle.setAttribute("aria-pressed", isPressed ? "false" : "true");
|
|
10135
|
+
}
|
|
10136
|
+
function setPressed(index, pressed) {
|
|
10137
|
+
if (index < 0 || index >= toggles.length) {
|
|
10138
|
+
console.error(`[aria-ease] Invalid toggle index: ${index}`);
|
|
10139
|
+
return;
|
|
10140
|
+
}
|
|
10141
|
+
toggles[index].setAttribute("aria-pressed", pressed ? "true" : "false");
|
|
10142
|
+
}
|
|
10143
|
+
function handleToggleClick(index) {
|
|
10144
|
+
return () => {
|
|
10145
|
+
toggleButton(index);
|
|
10146
|
+
};
|
|
10147
|
+
}
|
|
10148
|
+
function handleToggleKeydown(index) {
|
|
10149
|
+
return (event) => {
|
|
10150
|
+
const { key } = event;
|
|
10151
|
+
switch (key) {
|
|
10152
|
+
case "Enter":
|
|
10153
|
+
case " ":
|
|
10154
|
+
event.preventDefault();
|
|
10155
|
+
toggleButton(index);
|
|
10156
|
+
break;
|
|
10157
|
+
case "ArrowDown":
|
|
10158
|
+
case "ArrowRight":
|
|
10159
|
+
if (!isSingleToggle && toggles.length > 1) {
|
|
10160
|
+
event.preventDefault();
|
|
10161
|
+
const nextIndex = (index + 1) % toggles.length;
|
|
10162
|
+
toggles[nextIndex].focus();
|
|
10163
|
+
}
|
|
10164
|
+
break;
|
|
10165
|
+
case "ArrowUp":
|
|
10166
|
+
case "ArrowLeft":
|
|
10167
|
+
if (!isSingleToggle && toggles.length > 1) {
|
|
10168
|
+
event.preventDefault();
|
|
10169
|
+
const prevIndex = (index - 1 + toggles.length) % toggles.length;
|
|
10170
|
+
toggles[prevIndex].focus();
|
|
10171
|
+
}
|
|
10172
|
+
break;
|
|
10173
|
+
case "Home":
|
|
10174
|
+
if (!isSingleToggle && toggles.length > 1) {
|
|
10175
|
+
event.preventDefault();
|
|
10176
|
+
toggles[0].focus();
|
|
10177
|
+
}
|
|
10178
|
+
break;
|
|
10179
|
+
case "End":
|
|
10180
|
+
if (!isSingleToggle && toggles.length > 1) {
|
|
10181
|
+
event.preventDefault();
|
|
10182
|
+
toggles[toggles.length - 1].focus();
|
|
10183
|
+
}
|
|
10184
|
+
break;
|
|
10185
|
+
}
|
|
10186
|
+
};
|
|
10187
|
+
}
|
|
10188
|
+
function addListeners() {
|
|
10189
|
+
toggles.forEach((toggle, index) => {
|
|
10190
|
+
const clickHandler = handleToggleClick(index);
|
|
10191
|
+
const keydownHandler = handleToggleKeydown(index);
|
|
10192
|
+
toggle.addEventListener("click", clickHandler);
|
|
10193
|
+
toggle.addEventListener("keydown", keydownHandler);
|
|
10194
|
+
handlerMap.set(toggle, keydownHandler);
|
|
10195
|
+
clickHandlerMap.set(toggle, clickHandler);
|
|
10196
|
+
});
|
|
10197
|
+
}
|
|
10198
|
+
function removeListeners() {
|
|
10199
|
+
toggles.forEach((toggle) => {
|
|
10200
|
+
const keydownHandler = handlerMap.get(toggle);
|
|
10201
|
+
const clickHandler = clickHandlerMap.get(toggle);
|
|
10202
|
+
if (keydownHandler) {
|
|
10203
|
+
toggle.removeEventListener("keydown", keydownHandler);
|
|
10204
|
+
handlerMap.delete(toggle);
|
|
10205
|
+
}
|
|
10206
|
+
if (clickHandler) {
|
|
10207
|
+
toggle.removeEventListener("click", clickHandler);
|
|
10208
|
+
clickHandlerMap.delete(toggle);
|
|
10209
|
+
}
|
|
10210
|
+
});
|
|
10211
|
+
}
|
|
10212
|
+
function cleanup() {
|
|
10213
|
+
removeListeners();
|
|
10214
|
+
}
|
|
10215
|
+
function getPressedStates() {
|
|
10216
|
+
return toggles.map((toggle) => toggle.getAttribute("aria-pressed") === "true");
|
|
10217
|
+
}
|
|
10218
|
+
function getPressedIndices() {
|
|
10219
|
+
return toggles.map((toggle, index) => toggle.getAttribute("aria-pressed") === "true" ? index : -1).filter((index) => index !== -1);
|
|
10220
|
+
}
|
|
10221
|
+
initialize();
|
|
10222
|
+
addListeners();
|
|
10223
|
+
return {
|
|
10224
|
+
toggleButton,
|
|
10225
|
+
setPressed,
|
|
10226
|
+
getPressedStates,
|
|
10227
|
+
getPressedIndices,
|
|
10228
|
+
cleanup
|
|
10229
|
+
};
|
|
10230
|
+
}
|
|
10231
|
+
|
|
9683
10232
|
// src/combobox/src/makeComboBoxAccessible.ts
|
|
9684
10233
|
function makeComboboxAccessible({ comboboxInputId, comboboxButtonId, listBoxId, listBoxItemsClass, config: config2 }) {
|
|
9685
10234
|
const comboboxInput = document.getElementById(`${comboboxInputId}`);
|
|
@@ -13479,20 +14028,54 @@ async function runContractTests(componentName, component) {
|
|
|
13479
14028
|
const staticFailed = 0;
|
|
13480
14029
|
reporter.reportStatic(staticPassed, staticFailed);
|
|
13481
14030
|
reporter.summary(failures);
|
|
13482
|
-
return { passes, failures };
|
|
14031
|
+
return { passes, failures, skipped };
|
|
13483
14032
|
}
|
|
13484
14033
|
|
|
13485
14034
|
// src/utils/test/src/test.ts
|
|
13486
14035
|
async function testUiComponent(componentName, component, url) {
|
|
13487
|
-
|
|
14036
|
+
if (!componentName || typeof componentName !== "string") {
|
|
14037
|
+
throw new Error("\u274C testUiComponent requires a valid componentName (string)");
|
|
14038
|
+
}
|
|
14039
|
+
if (!component || !(component instanceof HTMLElement)) {
|
|
14040
|
+
throw new Error("\u274C testUiComponent requires a valid component (HTMLElement)");
|
|
14041
|
+
}
|
|
14042
|
+
if (url && typeof url !== "string") {
|
|
14043
|
+
throw new Error("\u274C testUiComponent url parameter must be a string");
|
|
14044
|
+
}
|
|
14045
|
+
let results;
|
|
14046
|
+
try {
|
|
14047
|
+
results = await axe(component);
|
|
14048
|
+
} catch (error) {
|
|
14049
|
+
throw new Error(
|
|
14050
|
+
`\u274C Axe accessibility scan failed
|
|
14051
|
+
Error: ${error instanceof Error ? error.message : String(error)}`
|
|
14052
|
+
);
|
|
14053
|
+
}
|
|
13488
14054
|
let contract;
|
|
13489
|
-
|
|
13490
|
-
|
|
13491
|
-
|
|
13492
|
-
|
|
13493
|
-
|
|
13494
|
-
|
|
13495
|
-
|
|
14055
|
+
try {
|
|
14056
|
+
if (url) {
|
|
14057
|
+
console.log(`\u{1F3AD} Running Playwright E2E tests on ${url}`);
|
|
14058
|
+
try {
|
|
14059
|
+
new URL(url);
|
|
14060
|
+
} catch {
|
|
14061
|
+
throw new Error(
|
|
14062
|
+
`\u274C Invalid URL format: "${url}"
|
|
14063
|
+
URL must include protocol (e.g., "http://localhost:5173/test")`
|
|
14064
|
+
);
|
|
14065
|
+
}
|
|
14066
|
+
const { runContractTestsPlaywright } = await import("./contractTestRunnerPlaywright-UQQI5MYS.js");
|
|
14067
|
+
contract = await runContractTestsPlaywright(componentName, url);
|
|
14068
|
+
} else {
|
|
14069
|
+
console.log(`\u{1F9EA} Running jsdom tests (limited event handling)`);
|
|
14070
|
+
console.log(`Some tests may be skipped or yield false positives/negatives.
|
|
14071
|
+
For full coverage, run with a URL to enable Playwright E2E tests.`);
|
|
14072
|
+
contract = await runContractTests(componentName, component);
|
|
14073
|
+
}
|
|
14074
|
+
} catch (error) {
|
|
14075
|
+
if (error instanceof Error) {
|
|
14076
|
+
throw error;
|
|
14077
|
+
}
|
|
14078
|
+
throw new Error(`\u274C Contract test execution failed: ${String(error)}`);
|
|
13496
14079
|
}
|
|
13497
14080
|
const result = {
|
|
13498
14081
|
violations: results.violations,
|
|
@@ -13503,10 +14086,11 @@ async function testUiComponent(componentName, component, url) {
|
|
|
13503
14086
|
const mode = url ? "Playwright" : "jsdom";
|
|
13504
14087
|
throw new Error(
|
|
13505
14088
|
`
|
|
13506
|
-
\u274C ${contract.failures.length}
|
|
13507
|
-
\u2705 ${contract.passes.length}
|
|
14089
|
+
\u274C ${contract.failures.length} accessibility contract test${contract.failures.length > 1 ? "s" : ""} failed (${mode} mode)
|
|
14090
|
+
\u2705 ${contract.passes.length} test${contract.passes.length > 1 ? "s" : ""} passed
|
|
13508
14091
|
|
|
13509
|
-
\u{1F4CB} Review the detailed test report above for specific failures
|
|
14092
|
+
\u{1F4CB} Review the detailed test report above for specific failures.
|
|
14093
|
+
\u{1F4A1} Contract tests validate ARIA attributes and keyboard interactions per W3C APG guidelines.`
|
|
13510
14094
|
);
|
|
13511
14095
|
}
|
|
13512
14096
|
if (results.violations.length > 0) {
|
|
@@ -13553,9 +14137,13 @@ if (typeof window === "undefined") {
|
|
|
13553
14137
|
};
|
|
13554
14138
|
}
|
|
13555
14139
|
export {
|
|
14140
|
+
makeAccordionAccessible,
|
|
13556
14141
|
makeBlockAccessible,
|
|
14142
|
+
makeCheckboxAccessible,
|
|
13557
14143
|
makeComboboxAccessible,
|
|
13558
14144
|
makeMenuAccessible,
|
|
14145
|
+
makeRadioAccessible,
|
|
14146
|
+
makeToggleAccessible,
|
|
13559
14147
|
testUiComponent,
|
|
13560
14148
|
updateAccordionTriggerAriaAttributes,
|
|
13561
14149
|
updateCheckboxAriaAttributes,
|