-
Notifications
You must be signed in to change notification settings - Fork 776
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
…#1335) * 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 * feat: rule for label and name from content mismatch * fix: refactor based on review and add unicode computation * refactor: update based on code review * test: update test * chore: fix linting errors * refactor: updates based on code review * refactor: unicode and punctuation methods * test: update tests
- Loading branch information
1 parent
70b30fc
commit a4255da
Showing
18 changed files
with
972 additions
and
8 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 |
---|---|---|
@@ -0,0 +1,49 @@ | ||
const { text } = axe.commons; | ||
|
||
const accText = text.accessibleText(node).toLowerCase(); | ||
if (text.isHumanInterpretable(accText) < 1) { | ||
return undefined; | ||
} | ||
|
||
const visibleText = text | ||
.sanitize(text.visibleVirtual(virtualNode)) | ||
.toLowerCase(); | ||
if (text.isHumanInterpretable(visibleText) < 1) { | ||
if (isStringContained(visibleText, accText)) { | ||
return true; | ||
} | ||
return undefined; | ||
} | ||
|
||
return isStringContained(visibleText, accText); | ||
|
||
/** | ||
* Check if a given text exists in another | ||
* | ||
* @param {String} compare given text to check | ||
* @param {String} compareWith text against which to be compared | ||
* @returns {Boolean} | ||
*/ | ||
function isStringContained(compare, compareWith) { | ||
const curatedCompareWith = curateString(compareWith); | ||
const curatedCompare = curateString(compare); | ||
if (!curatedCompareWith || !curatedCompare) { | ||
return false; | ||
} | ||
return curatedCompareWith.includes(curatedCompare); | ||
} | ||
|
||
/** | ||
* Curate given text, by removing emoji's, punctuations, unicode and trim whitespace. | ||
* | ||
* @param {String} str given text to curate | ||
* @returns {String} | ||
*/ | ||
function curateString(str) { | ||
const noUnicodeStr = text.removeUnicode(str, { | ||
emoji: true, | ||
nonBmp: true, | ||
punctuations: true | ||
}); | ||
return text.sanitize(noUnicodeStr); | ||
} |
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,11 @@ | ||
{ | ||
"id": "label-content-name-mismatch", | ||
"evaluate": "label-content-name-mismatch.js", | ||
"metadata": { | ||
"impact": "serious", | ||
"messages": { | ||
"pass": "Element contains visible text as part of it's accessible name", | ||
"fail": "Text inside the element is not included in the accessible name" | ||
} | ||
} | ||
} |
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 |
---|---|---|
@@ -0,0 +1,51 @@ | ||
/* global text */ | ||
|
||
/** | ||
* Determines if a given text is human friendly and interpretable | ||
* | ||
* @method isHumanInterpretable | ||
* @memberof axe.commons.text | ||
* @instance | ||
* @param {String} str text to be validated | ||
* @returns {Number} Between 0 and 1, (0 -> not interpretable, 1 -> interpretable) | ||
*/ | ||
text.isHumanInterpretable = function(str) { | ||
/** | ||
* Steps: | ||
* 1) Check for single character edge cases | ||
* a) handle if character is alphanumeric & within the given icon mapping | ||
* eg: x (close), i (info) | ||
* | ||
* 3) handle unicode from astral (non bilingual multi plane) unicode, emoji & punctuations | ||
* eg: Windings font | ||
* eg: '💪' | ||
* eg: I saw a shooting 💫 | ||
* eg: ? (help), > (next arrow), < (back arrow), need help ? | ||
*/ | ||
|
||
if (!str.length) { | ||
return 0; | ||
} | ||
|
||
// Step 1 | ||
const alphaNumericIconMap = [ | ||
'x', // close | ||
'i' // info | ||
]; | ||
// Step 1a | ||
if (alphaNumericIconMap.includes(str)) { | ||
return 0; | ||
} | ||
|
||
// Step 2 | ||
const noUnicodeStr = text.removeUnicode(str, { | ||
emoji: true, | ||
nonBmp: true, | ||
punctuations: true | ||
}); | ||
if (!text.sanitize(noUnicodeStr)) { | ||
return 0; | ||
} | ||
|
||
return 1; | ||
}; |
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,117 @@ | ||
/* global text */ | ||
|
||
/** | ||
* Determine if a given string contains unicode characters, specified in options | ||
* | ||
* @method hasUnicode | ||
* @memberof axe.commons.text | ||
* @instance | ||
* @param {String} str string to verify | ||
* @param {Object} options config containing which unicode character sets to verify | ||
* @property {Boolean} options.emoji verify emoji unicode | ||
* @property {Boolean} options.nonBmp verify nonBmp unicode | ||
* @property {Boolean} options.punctuations verify punctuations unicode | ||
* @returns {Boolean} | ||
*/ | ||
text.hasUnicode = function hasUnicode(str, options) { | ||
const { emoji, nonBmp, punctuations } = options; | ||
if (emoji) { | ||
return axe.imports.emojiRegexText().test(str); | ||
} | ||
if (nonBmp) { | ||
return getUnicodeNonBmpRegExp().test(str); | ||
} | ||
if (punctuations) { | ||
return getPunctuationRegExp().test(str); | ||
} | ||
return false; | ||
}; | ||
|
||
/** | ||
* Remove specified type(s) unicode characters | ||
* | ||
* @method removeUnicode | ||
* @memberof axe.commons.text | ||
* @instance | ||
* @param {String} str string to operate on | ||
* @param {Object} options config containing which unicode character sets to remove | ||
* @property {Boolean} options.emoji remove emoji unicode | ||
* @property {Boolean} options.nonBmp remove nonBmp unicode | ||
* @property {Boolean} options.punctuations remove punctuations unicode | ||
* @returns {String} | ||
*/ | ||
text.removeUnicode = function removeUnicode(str, options) { | ||
const { emoji, nonBmp, punctuations } = options; | ||
|
||
if (emoji) { | ||
str = str.replace(axe.imports.emojiRegexText(), ''); | ||
} | ||
if (nonBmp) { | ||
str = str.replace(getUnicodeNonBmpRegExp(), ''); | ||
} | ||
if (punctuations) { | ||
str = str.replace(getPunctuationRegExp(), ''); | ||
} | ||
|
||
return str; | ||
}; | ||
|
||
/** | ||
* Regex for matching unicode values out of Basic Multilingual Plane (BMP) | ||
* Reference: | ||
* - https://github.com/mathiasbynens/regenerate | ||
* - https://unicode-table.com/ | ||
* - https://mathiasbynens.be/notes/javascript-unicode | ||
* | ||
* @returns {RegExp} | ||
*/ | ||
function getUnicodeNonBmpRegExp() { | ||
/** | ||
* Regex for matching astral plane unicode | ||
* - http://kourge.net/projects/regexp-unicode-block | ||
*/ | ||
return new RegExp( | ||
'[' + | ||
'\u1D00-\u1D7F' + // Phonetic Extensions | ||
'\u1D80-\u1DBF' + // Phonetic Extensions Supplement | ||
'\u1DC0-\u1DFF' + // Combining Diacritical Marks Supplement | ||
// '\u2000-\u206F' + // General punctuation - handled in -> getPunctuationRegExp | ||
'\u20A0-\u20CF' + // Currency symbols | ||
'\u20D0-\u20FF' + // Combining Diacritical Marks for Symbols | ||
'\u2100-\u214F' + // Letter like symbols | ||
'\u2150-\u218F' + // Number forms (eg: Roman numbers) | ||
'\u2190-\u21FF' + // Arrows | ||
'\u2200-\u22FF' + // Mathematical operators | ||
'\u2300-\u23FF' + // Misc Technical | ||
'\u2400-\u243F' + // Control pictures | ||
'\u2440-\u245F' + // OCR | ||
'\u2460-\u24FF' + // Enclosed alpha numerics | ||
'\u2500-\u257F' + // Box Drawing | ||
'\u2580-\u259F' + // Block Elements | ||
'\u25A0-\u25FF' + // Geometric Shapes | ||
'\u2600-\u26FF' + // Misc Symbols | ||
'\u2700-\u27BF' + // Dingbats | ||
']' | ||
); | ||
} | ||
|
||
/** | ||
* Get regular expression for matching punctuations | ||
* | ||
* @returns {RegExp} | ||
*/ | ||
function getPunctuationRegExp() { | ||
/** | ||
* Reference: http://kunststube.net/encoding/ | ||
* US-ASCII | ||
* -> !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~ | ||
* | ||
* General Punctuation block | ||
* -> \u2000-\u206F | ||
* | ||
* Supplemental Punctuation block | ||
* Reference: https://en.wikipedia.org/wiki/Supplemental_Punctuation | ||
* -> \u2E00-\u2E7F Reference | ||
*/ | ||
return /[\u2000-\u206F\u2E00-\u2E7F\\'!"#$%&()*+,\-.\/:;<=>?@\[\]^_`{|}~]/g; | ||
} |
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 |
---|---|---|
@@ -0,0 +1,42 @@ | ||
/** | ||
* Applicability: | ||
* Rule applies to any element that has | ||
* a) a semantic role that is `widget` that supports name from content | ||
* b) has visible text content | ||
* c) has accessible name (eg: `aria-label`) | ||
*/ | ||
const { aria, text } = axe.commons; | ||
|
||
const role = aria.getRole(node); | ||
if (!role) { | ||
return false; | ||
} | ||
|
||
const isWidgetType = aria.lookupTable.rolesOfType.widget.includes(role); | ||
if (!isWidgetType) { | ||
return false; | ||
} | ||
|
||
const rolesWithNameFromContents = aria.getRolesWithNameFromContents(); | ||
if (!rolesWithNameFromContents.includes(role)) { | ||
return false; | ||
} | ||
|
||
/** | ||
* if no `aria-label` or `aria-labelledby` attribute - ignore `node` | ||
*/ | ||
if ( | ||
!text.sanitize(aria.arialabelText(node)) && | ||
!text.sanitize(aria.arialabelledbyText(node)) | ||
) { | ||
return false; | ||
} | ||
|
||
/** | ||
* if no `contentText` - ignore `node` | ||
*/ | ||
if (!text.sanitize(text.visibleVirtual(virtualNode))) { | ||
return false; | ||
} | ||
|
||
return true; |
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,12 @@ | ||
{ | ||
"id": "label-content-name-mismatch", | ||
"matches": "label-content-name-mismatch-matches.js", | ||
"tags": ["wcag21a", "wcag253", "experimental"], | ||
"metadata": { | ||
"description": "Ensures that elements labelled through their content must have their visible text as part of their accessible name", | ||
"help": "Elements must have their visible text as part of their accessible name" | ||
}, | ||
"all": [], | ||
"any": ["label-content-name-mismatch"], | ||
"none": [] | ||
} |
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
Oops, something went wrong.