@vaadin/a11y-base
Advanced tools
+5
-5
| { | ||
| "name": "@vaadin/a11y-base", | ||
| "version": "25.3.0-alpha6", | ||
| "version": "25.3.0-alpha7", | ||
| "publishConfig": { | ||
@@ -35,8 +35,8 @@ "access": "public" | ||
| "@open-wc/dedupe-mixin": "^1.3.0", | ||
| "@vaadin/component-base": "25.3.0-alpha6", | ||
| "@vaadin/component-base": "25.3.0-alpha7", | ||
| "lit": "^3.0.0" | ||
| }, | ||
| "devDependencies": { | ||
| "@vaadin/chai-plugins": "25.3.0-alpha6", | ||
| "@vaadin/test-runner-commands": "25.3.0-alpha6", | ||
| "@vaadin/chai-plugins": "25.3.0-alpha7", | ||
| "@vaadin/test-runner-commands": "25.3.0-alpha7", | ||
| "@vaadin/testing-helpers": "^2.0.0", | ||
@@ -46,3 +46,3 @@ "sinon": "^22.0.0" | ||
| "customElements": "custom-elements.json", | ||
| "gitHead": "92c124fb9cff367bc07e734d8e65707facd0bd43" | ||
| "gitHead": "ae7b9823df5598faebd7a482993029ee489c35ae" | ||
| } |
@@ -16,2 +16,5 @@ /** | ||
| export class AriaModalController { | ||
| /** @type {Function | null} */ | ||
| #showOthers = null; | ||
| /** | ||
@@ -46,3 +49,3 @@ * @param {HTMLElement} host | ||
| const targets = this.callback(); | ||
| this.__showOthers = hideOthers(targets); | ||
| this.#showOthers = hideOthers(targets); | ||
| } | ||
@@ -55,7 +58,7 @@ | ||
| close() { | ||
| if (this.__showOthers) { | ||
| this.__showOthers(); | ||
| this.__showOthers = null; | ||
| if (this.#showOthers) { | ||
| this.#showOthers(); | ||
| this.#showOthers = null; | ||
| } | ||
| } | ||
| } |
@@ -13,5 +13,25 @@ /** | ||
| export class FieldAriaController { | ||
| /** @type {HTMLElement | undefined} */ | ||
| #target; | ||
| /** @type {boolean} */ | ||
| #required = false; | ||
| /** @type {string | null | undefined} */ | ||
| #label; | ||
| /** @type {string | null | undefined} */ | ||
| #labelId; | ||
| /** @type {string | null | undefined} */ | ||
| #labelIdFromUser; | ||
| /** @type {string | null | undefined} */ | ||
| #errorId; | ||
| /** @type {string | null | undefined} */ | ||
| #helperId; | ||
| constructor(host) { | ||
| this.host = host; | ||
| this.__required = false; | ||
| } | ||
@@ -25,12 +45,12 @@ | ||
| setTarget(target) { | ||
| this.__target = target; | ||
| this.__setAriaRequiredAttribute(this.__required); | ||
| // We need to make sure that value in __labelId is stored | ||
| this.__setLabelIdToAriaAttribute(this.__labelId, this.__labelId); | ||
| if (this.__labelIdFromUser != null) { | ||
| this.__setLabelIdToAriaAttribute(this.__labelIdFromUser, this.__labelIdFromUser, true); | ||
| this.#target = target; | ||
| this.#setAriaRequiredAttribute(this.#required); | ||
| // We need to make sure that value in #labelId is stored | ||
| this.#setLabelIdToAriaAttribute(this.#labelId, this.#labelId); | ||
| if (this.#labelIdFromUser != null) { | ||
| this.#setLabelIdToAriaAttribute(this.#labelIdFromUser, this.#labelIdFromUser, true); | ||
| } | ||
| this.__setErrorIdToAriaAttribute(this.__errorId); | ||
| this.__setHelperIdToAriaAttribute(this.__helperId); | ||
| this.setAriaLabel(this.__label); | ||
| this.#setErrorIdToAriaAttribute(this.#errorId); | ||
| this.#setHelperIdToAriaAttribute(this.#helperId); | ||
| this.setAriaLabel(this.#label); | ||
| } | ||
@@ -46,4 +66,4 @@ | ||
| setRequired(required) { | ||
| this.__setAriaRequiredAttribute(required); | ||
| this.__required = required; | ||
| this.#setAriaRequiredAttribute(required); | ||
| this.#required = required; | ||
| } | ||
@@ -59,4 +79,4 @@ | ||
| setAriaLabel(label) { | ||
| this.__setAriaLabelToAttribute(label); | ||
| this.__label = label; | ||
| this.#setAriaLabelToAttribute(label); | ||
| this.#label = label; | ||
| } | ||
@@ -73,8 +93,8 @@ | ||
| setLabelId(labelId, fromUser = false) { | ||
| const oldLabelId = fromUser ? this.__labelIdFromUser : this.__labelId; | ||
| this.__setLabelIdToAriaAttribute(labelId, oldLabelId, fromUser); | ||
| const oldLabelId = fromUser ? this.#labelIdFromUser : this.#labelId; | ||
| this.#setLabelIdToAriaAttribute(labelId, oldLabelId, fromUser); | ||
| if (fromUser) { | ||
| this.__labelIdFromUser = labelId; | ||
| this.#labelIdFromUser = labelId; | ||
| } else { | ||
| this.__labelId = labelId; | ||
| this.#labelId = labelId; | ||
| } | ||
@@ -93,4 +113,4 @@ } | ||
| setErrorId(errorId) { | ||
| this.__setErrorIdToAriaAttribute(errorId, this.__errorId); | ||
| this.__errorId = errorId; | ||
| this.#setErrorIdToAriaAttribute(errorId, this.#errorId); | ||
| this.#errorId = errorId; | ||
| } | ||
@@ -108,4 +128,4 @@ | ||
| setHelperId(helperId) { | ||
| this.__setHelperIdToAriaAttribute(helperId, this.__helperId); | ||
| this.__helperId = helperId; | ||
| this.#setHelperIdToAriaAttribute(helperId, this.#helperId); | ||
| this.#helperId = helperId; | ||
| } | ||
@@ -115,14 +135,13 @@ | ||
| * @param {string | null | undefined} label | ||
| * @private | ||
| * */ | ||
| __setAriaLabelToAttribute(label) { | ||
| if (!this.__target) { | ||
| */ | ||
| #setAriaLabelToAttribute(label) { | ||
| if (!this.#target) { | ||
| return; | ||
| } | ||
| if (label) { | ||
| removeAriaIDReference(this.__target, 'aria-labelledby'); | ||
| this.__target.setAttribute('aria-label', label); | ||
| } else if (this.__label) { | ||
| restoreGeneratedAriaIDReference(this.__target, 'aria-labelledby'); | ||
| this.__target.removeAttribute('aria-label'); | ||
| removeAriaIDReference(this.#target, 'aria-labelledby'); | ||
| this.#target.setAttribute('aria-label', label); | ||
| } else if (this.#label) { | ||
| restoreGeneratedAriaIDReference(this.#target, 'aria-labelledby'); | ||
| this.#target.removeAttribute('aria-label'); | ||
| } | ||
@@ -135,6 +154,5 @@ } | ||
| * @param {boolean | null | undefined} fromUser | ||
| * @private | ||
| */ | ||
| __setLabelIdToAriaAttribute(labelId, oldLabelId, fromUser) { | ||
| setAriaIDReference(this.__target, 'aria-labelledby', { newId: labelId, oldId: oldLabelId, fromUser }); | ||
| #setLabelIdToAriaAttribute(labelId, oldLabelId, fromUser) { | ||
| setAriaIDReference(this.#target, 'aria-labelledby', { newId: labelId, oldId: oldLabelId, fromUser }); | ||
| } | ||
@@ -145,6 +163,5 @@ | ||
| * @param {string | null | undefined} oldErrorId | ||
| * @private | ||
| */ | ||
| __setErrorIdToAriaAttribute(errorId, oldErrorId) { | ||
| setAriaIDReference(this.__target, 'aria-describedby', { newId: errorId, oldId: oldErrorId, fromUser: false }); | ||
| #setErrorIdToAriaAttribute(errorId, oldErrorId) { | ||
| setAriaIDReference(this.#target, 'aria-describedby', { newId: errorId, oldId: oldErrorId, fromUser: false }); | ||
| } | ||
@@ -155,6 +172,5 @@ | ||
| * @param {string | null | undefined} oldHelperId | ||
| * @private | ||
| */ | ||
| __setHelperIdToAriaAttribute(helperId, oldHelperId) { | ||
| setAriaIDReference(this.__target, 'aria-describedby', { newId: helperId, oldId: oldHelperId, fromUser: false }); | ||
| #setHelperIdToAriaAttribute(helperId, oldHelperId) { | ||
| setAriaIDReference(this.#target, 'aria-describedby', { newId: helperId, oldId: oldHelperId, fromUser: false }); | ||
| } | ||
@@ -164,10 +180,9 @@ | ||
| * @param {boolean} required | ||
| * @private | ||
| */ | ||
| __setAriaRequiredAttribute(required) { | ||
| if (!this.__target) { | ||
| #setAriaRequiredAttribute(required) { | ||
| if (!this.#target) { | ||
| return; | ||
| } | ||
| if (['input', 'textarea'].includes(this.__target.localName)) { | ||
| if (['input', 'textarea'].includes(this.#target.localName)) { | ||
| // Native <input> or <textarea>, required is enough | ||
@@ -178,7 +193,7 @@ return; | ||
| if (required) { | ||
| this.__target.setAttribute('aria-required', 'true'); | ||
| this.#target.setAttribute('aria-required', 'true'); | ||
| } else { | ||
| this.__target.removeAttribute('aria-required'); | ||
| this.#target.removeAttribute('aria-required'); | ||
| } | ||
| } | ||
| } |
@@ -23,2 +23,7 @@ /** | ||
| /** | ||
| * A node for trapping focus in. | ||
| */ | ||
| trapNode: HTMLElement | null; | ||
| constructor(node: HTMLElement); | ||
@@ -25,0 +30,0 @@ |
@@ -20,4 +20,4 @@ /** | ||
| for (let i = instances.length - 1; i >= 0; i--) { | ||
| if (instances[i].__trapNode?.contains(element)) { | ||
| return instances[i].__trapNode; | ||
| if (instances[i].trapNode?.contains(element)) { | ||
| return instances[i].trapNode; | ||
| } | ||
@@ -33,2 +33,9 @@ } | ||
| /** | ||
| * A node for trapping focus in. | ||
| * | ||
| * @type {HTMLElement | null} | ||
| */ | ||
| trapNode = null; | ||
| /** | ||
| * @param {HTMLElement} host | ||
@@ -43,12 +50,2 @@ */ | ||
| this.host = host; | ||
| /** | ||
| * A node for trapping focus in. | ||
| * | ||
| * @type {HTMLElement | null} | ||
| * @private | ||
| */ | ||
| this.__trapNode = null; | ||
| this.__onKeyDown = this.__onKeyDown.bind(this); | ||
| } | ||
@@ -60,6 +57,5 @@ | ||
| * @return {HTMLElement[]} | ||
| * @private | ||
| */ | ||
| get __focusableElements() { | ||
| return getTabbableElements(this.__trapNode); | ||
| get #focusableElements() { | ||
| return getTabbableElements(this.trapNode); | ||
| } | ||
@@ -71,6 +67,5 @@ | ||
| * @return {HTMLElement | undefined} | ||
| * @private | ||
| */ | ||
| get __focusedElementIndex() { | ||
| const focusableElements = this.__focusableElements; | ||
| get #focusedElementIndex() { | ||
| const focusableElements = this.#focusableElements; | ||
| return focusableElements.indexOf(focusableElements.filter(isElementFocused).pop()); | ||
@@ -80,7 +75,7 @@ } | ||
| hostConnected() { | ||
| document.addEventListener('keydown', this.__onKeyDown); | ||
| document.addEventListener('keydown', this.#onKeyDown); | ||
| } | ||
| hostDisconnected() { | ||
| document.removeEventListener('keydown', this.__onKeyDown); | ||
| document.removeEventListener('keydown', this.#onKeyDown); | ||
| } | ||
@@ -103,6 +98,6 @@ | ||
| trapFocus(trapNode) { | ||
| this.__trapNode = trapNode; | ||
| this.trapNode = trapNode; | ||
| if (this.__focusableElements.length === 0) { | ||
| this.__trapNode = null; | ||
| if (this.#focusableElements.length === 0) { | ||
| this.trapNode = null; | ||
| throw new Error('The trap node should have at least one focusable descendant or be focusable itself.'); | ||
@@ -113,4 +108,4 @@ } | ||
| if (this.__focusedElementIndex === -1) { | ||
| this.__focusableElements[0].focus({ focusVisible: isKeyboardActive() }); | ||
| if (this.#focusedElementIndex === -1) { | ||
| this.#focusableElements[0].focus({ focusVisible: isKeyboardActive() }); | ||
| } | ||
@@ -124,3 +119,3 @@ } | ||
| releaseFocus() { | ||
| this.__trapNode = null; | ||
| this.trapNode = null; | ||
@@ -139,6 +134,5 @@ instances.pop(); | ||
| * @param {KeyboardEvent} event | ||
| * @private | ||
| */ | ||
| __onKeyDown(event) { | ||
| if (!this.__trapNode) { | ||
| #onKeyDown = (event) => { | ||
| if (!this.trapNode) { | ||
| return; | ||
@@ -161,5 +155,5 @@ } | ||
| const backward = event.shiftKey; | ||
| this.__focusNextElement(backward); | ||
| this.#focusNextElement(backward); | ||
| } | ||
| } | ||
| }; | ||
@@ -175,8 +169,7 @@ /** | ||
| * @param {boolean} backward | ||
| * @private | ||
| */ | ||
| __focusNextElement(backward = false) { | ||
| const focusableElements = this.__focusableElements; | ||
| #focusNextElement(backward = false) { | ||
| const focusableElements = this.#focusableElements; | ||
| const step = backward ? -1 : 1; | ||
| const currentIndex = this.__focusedElementIndex; | ||
| const currentIndex = this.#focusedElementIndex; | ||
| const nextIndex = (focusableElements.length + currentIndex + step) % focusableElements.length; | ||
@@ -183,0 +176,0 @@ const element = focusableElements[nextIndex]; |
120083
0.17%3311
0.24%+ Added
- Removed