-
Notifications
You must be signed in to change notification settings - Fork 795
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Rebuild the accessible text algorithm (#1163)
* chore(WIP): rewrite accessibleText * chore: More refactoring for accname * chore(WIP): More improvements to accessibleName * feat: Reimplement accessible name computation * chore: All accessible name tests passing * chore(accName): All tests passing * chore: Add tests * chore: Test form-control-value * chore: Refactor and add docs to accessible-text * chore: Add tests for namedFromContents * chore: Refactor subtreeText method * chore: Refactor native accessible text methods * chore: Coverage for text.labelText * fix: update to axe.commons.matches usage * test: fix nativeTextboxValue tests * test: fix failing tests * fix: compute includeHidden as a part of accessibleName fn * fix: do not mutate context in accessibleText * chore: Refactor a11yText method for readability * chore: Update a11yText test results
- Loading branch information
1 parent
0c2f42d
commit 5f420e5
Showing
33 changed files
with
4,255 additions
and
491 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,7 @@ | ||
var label = axe.commons.dom.findUpVirtual(virtualNode, 'label'); | ||
const { dom, text } = axe.commons; | ||
|
||
var label = dom.findUpVirtual(virtualNode, 'label'); | ||
if (label) { | ||
return !!axe.commons.text.accessibleTextVirtual(label); | ||
return !!text.accessibleText(label, { inControlContext: true }); | ||
} | ||
return false; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
var label = node.getAttribute('aria-label'); | ||
return !!(label ? axe.commons.text.sanitize(label).trim() : ''); | ||
const { text, aria } = axe.commons; | ||
return !!text.sanitize(aria.arialabelText(node)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,2 @@ | ||
var getIdRefs = axe.commons.dom.idrefs; | ||
|
||
return getIdRefs(node, 'aria-labelledby').some(function(elm) { | ||
return elm && axe.commons.text.accessibleText(elm, true); | ||
}); | ||
const { text, aria } = axe.commons; | ||
return !!text.sanitize(aria.arialabelledbyText(node)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,2 @@ | ||
var title = node.getAttribute('title'); | ||
return !!(title ? axe.commons.text.sanitize(title).trim() : ''); | ||
const { text } = axe.commons; | ||
return !!text.sanitize(text.titleText(node)); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
/* global aria */ | ||
|
||
/** | ||
* Get the text value of aria-label, if any | ||
* | ||
* @param {VirtualNode|Element} element | ||
* @return {string} ARIA label | ||
*/ | ||
aria.arialabelText = function arialabelText(node) { | ||
node = node.actualNode || node; | ||
if (node.nodeType !== 1) { | ||
return ''; | ||
} | ||
return node.getAttribute('aria-label') || ''; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
/* global aria, dom, text */ | ||
|
||
/** | ||
* Get the accessible name based on aria-labelledby | ||
* | ||
* @param {VirtualNode} element | ||
* @param {Object} context | ||
* @property {Bool} inLabelledByContext Whether or not the lookup is part of aria-labelledby reference | ||
* @property {Bool} inControlContext Whether or not the lookup is part of a native label reference | ||
* @property {Element} startNode First node in accessible name computation | ||
* @property {Bool} debug Enable logging for formControlValue | ||
* @return {string} Cancatinated text value for referenced elements | ||
*/ | ||
aria.arialabelledbyText = function arialabelledbyText(node, context = {}) { | ||
node = node.actualNode || node; | ||
/** | ||
* Note: The there are significant difference in how many "leads" browsers follow. | ||
* - Firefox stops after the first IDREF, so it | ||
* doesn't follow aria-labelledby after a for:>ID ref. | ||
* - Chrome seems to just keep iterating no matter how many levels deep. | ||
* - AccName-AAM 1.1 suggests going one level deep, but to treat | ||
* each ref type separately. | ||
* | ||
* Axe-core's implementation behaves most closely like Firefox as it seems | ||
* to be the most common deniminator. Main difference is that Firefox | ||
* includes the value of form controls in addition to aria-label(s), | ||
* something no other browser seems to do. Axe doesn't do that. | ||
*/ | ||
if ( | ||
node.nodeType !== 1 || | ||
context.inLabelledByContext || | ||
context.inControlContext | ||
) { | ||
return ''; | ||
} | ||
|
||
const refs = dom.idrefs(node, 'aria-labelledby').filter(elm => elm); | ||
return refs.reduce((accessibleName, elm) => { | ||
const accessibleNameAdd = text.accessibleText(elm, { | ||
// Prevent the infinite reference loop: | ||
inLabelledByContext: true, | ||
startNode: context.startNode || node, | ||
...context | ||
}); | ||
|
||
if (!accessibleName) { | ||
return accessibleNameAdd; | ||
} else { | ||
return `${accessibleName} ${accessibleNameAdd}`; | ||
} | ||
}, ''); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
/* global aria, dom */ | ||
|
||
/** | ||
* Get an element's owned elements | ||
* | ||
* @param {VirtualNode} element | ||
* @return {VirtualNode[]} Owned elements | ||
*/ | ||
aria.getOwnedVirtual = function getOwned({ actualNode, children }) { | ||
if (!actualNode || !children) { | ||
throw new Error('getOwnedVirtual requires a virtual node'); | ||
} | ||
// TODO: Check that the element has a role | ||
// TODO: Descend into children with role=presentation|none | ||
// TODO: Exclude descendents owned by other elements | ||
|
||
return dom.idrefs(actualNode, 'aria-owns').reduce((ownedElms, element) => { | ||
if (element) { | ||
const virtualNode = axe.utils.getNodeFromTree(axe._tree[0], element); | ||
ownedElms.push(virtualNode); | ||
} | ||
return ownedElms; | ||
}, children); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
/* global aria */ | ||
|
||
/** | ||
* Check if an element is named from contents | ||
* | ||
* @param {Node|VirtualNode} element | ||
* @param {Object} options | ||
* @property {Bool} strict Whether or not to follow the spects strictly | ||
* @return {Bool} | ||
*/ | ||
aria.namedFromContents = function namedFromContents(node, { strict } = {}) { | ||
node = node.actualNode || node; | ||
if (node.nodeType !== 1) { | ||
return false; | ||
} | ||
|
||
const role = aria.getRole(node); | ||
const roleDef = aria.lookupTable.role[role]; | ||
|
||
if ( | ||
(roleDef && roleDef.nameFrom.includes('contents')) || | ||
// TODO: This is a workaround for axe-core's over-assertive implicitRole computation | ||
// once we fix that, this extra noImplicit check can be removed. | ||
node.nodeName.toUpperCase() === 'TABLE' | ||
) { | ||
return true; | ||
} | ||
|
||
/** | ||
* Note: Strictly speaking if the role is null, presentation, or none, the element | ||
* isn't named from contents. Axe-core often needs to know if an element | ||
* has content anyway, so we're allowing it here. | ||
* Use { strict: true } to disable this behavior. | ||
*/ | ||
if (strict) { | ||
return false; | ||
} | ||
return !roleDef || ['presentation', 'none'].includes(role); | ||
}; |
Oops, something went wrong.