Skip to content

Commit

Permalink
fix(label-content-name-mismatch): better dismiss and wysiwyg symbolic…
Browse files Browse the repository at this point in the history
… text characters (#4402)

adds exceptions for (dismiss) `×`, wysiwyg characters `b`, `aA`, `abc`

fix: #4386

---

This does _not_ handle all potential use cases. Potential shortcomings
worth discussing and opening further issues for are as follows:

- Considering other languages, such as [this example in German including
"UT", "AD" or "DGS"](w3c/wcag#3304)
- The original issue #4386 mentioned this being handled as a character
limit, but it's unclear if that's a valid option. Consider [the WCAG
`ABC`
example](https://www.w3.org/WAI/WCAG21/Understanding/images-of-text-no-exception#examples)
- **EDIT**: Does not specifically have test cases for [math expressions
and
formulae](https://www.w3.org/WAI/WCAG21/Understanding/label-in-name.html#mathematical-expressions-and-formulae)
  • Loading branch information
gaiety-deque committed Apr 15, 2024
2 parents 5c318f3 + c4aa062 commit 56e139a
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 41 deletions.
15 changes: 6 additions & 9 deletions lib/checks/label/label-content-name-mismatch-evaluate.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,7 @@ function labelContentNameMismatchEvaluate(node, options, virtualNode) {
const pixelThreshold = options?.pixelThreshold;
const occurrenceThreshold =
options?.occurrenceThreshold ?? options?.occuranceThreshold;

const accText = accessibleText(node).toLowerCase();
if (isHumanInterpretable(accText) < 1) {
return undefined;
}

const visibleText = sanitize(
subtreeText(virtualNode, {
subtreeDescendant: true,
Expand All @@ -55,13 +50,15 @@ function labelContentNameMismatchEvaluate(node, options, virtualNode) {
occurrenceThreshold
})
).toLowerCase();

if (!visibleText) {
return true;
}
if (isHumanInterpretable(visibleText) < 1) {
if (isStringContained(visibleText, accText)) {
return true;
}

if (
isHumanInterpretable(accText) < 1 ||
isHumanInterpretable(visibleText) < 1
) {
return undefined;
}

Expand Down
50 changes: 33 additions & 17 deletions lib/commons/text/is-human-interpretable.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,42 +13,58 @@ import sanitize from './sanitize';
function isHumanInterpretable(str) {
/**
* Steps:
* 1) Check for single character edge cases
* 1) Early escape if string is empty
*
* 2) Check for single alpha character edge cases
*
* 3) Check for symbolic text character edge cases
* a) handle if character is alphanumeric & within the given icon mapping
* eg: x (close), i (info)
* eg: 'aA' for toggling capitalization
*
* 3) handle unicode from astral (non bilingual multi plane) unicode, emoji & punctuations
* 4) 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) {
if (
isEmpty(str) ||
isNonDigitCharacter(str) ||
isSymbolicText(str) ||
isUnicodeOrPunctuation(str)
) {
return 0;
}

// Step 1
const alphaNumericIconMap = [
'x', // close
'i' // info
return 1;
}

function isEmpty(str) {
return sanitize(str).length === 0;
}

function isNonDigitCharacter(str) {
return str.length === 1 && str.match(/\D/);
}

function isSymbolicText(str) {
const symbolicText = [
'aa', // toggle capitalization (aA)
'abc' // spelling
];
// Step 1a
if (alphaNumericIconMap.includes(str)) {
return 0;
}

// Step 2
return symbolicText.includes(str.toLowerCase());
}

function isUnicodeOrPunctuation(str) {
const noUnicodeStr = removeUnicode(str, {
emoji: true,
nonBmp: true,
punctuations: true
});
if (!sanitize(noUnicodeStr)) {
return 0;
}

return 1;
return !sanitize(noUnicodeStr);
}

export default isHumanInterpretable;
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,11 @@
"name": "Marcy Sutton",
"organization": "Deque Systems, Inc.",
"url": "http://deque.com/"
},
{
"name": "Ava Gaiety Wroten",
"organization": "Deque Systems, Inc.",
"url": "http://deque.com/"
}
],
"homepage": "https://www.deque.com/axe/",
Expand Down
41 changes: 27 additions & 14 deletions test/commons/text/is-human-interpretable.js
Original file line number Diff line number Diff line change
@@ -1,63 +1,76 @@
describe('text.isHumanInterpretable', function () {
it('returns 0 when given string is empty', function () {
var actual = axe.commons.text.isHumanInterpretable('');
const actual = axe.commons.text.isHumanInterpretable('');
assert.equal(actual, 0);
});

it('returns 0 when given string is a single character that is blacklisted as icon', function () {
var blacklistedIcons = ['x', 'i'];
blacklistedIcons.forEach(function (iconText) {
var actual = axe.commons.text.isHumanInterpretable(iconText);
it('returns 0 when given string is a single alpha character', function () {
const singleCharacterExamples = ['i', 'x', 'X', '×', ''];
singleCharacterExamples.forEach(function (characterExample) {
const actual = axe.commons.text.isHumanInterpretable(characterExample);
assert.equal(actual, 0);
});
});

it('returns 0 when given string is in the symbolic text characters set (blocklist)', function () {
const blocklistedSymbols = ['aA', 'Aa', 'abc', 'ABC'];
blocklistedSymbols.forEach(function (symbolicText) {
const actual = axe.commons.text.isHumanInterpretable(symbolicText);
assert.equal(actual, 0);
});
});

it('returns 0 when given string is only punctuations', function () {
var actual = axe.commons.text.isHumanInterpretable('?!!!,.');
const actual = axe.commons.text.isHumanInterpretable('?!!!,.');
assert.equal(actual, 0);
});

it('returns 1 when given string that has a number', function () {
const actual = axe.commons.text.isHumanInterpretable('7');
assert.equal(actual, 1);
});

it('returns 1 when given string has emoji as a part of the sentence', function () {
var actual = axe.commons.text.isHumanInterpretable('I like 🏀');
const actual = axe.commons.text.isHumanInterpretable('I like 🏀');
assert.equal(actual, 1);
});

it('returns 1 when given string has non BMP character (eg: windings font) as part of the sentence', function () {
var actual = axe.commons.text.isHumanInterpretable('I ✂ my hair');
const actual = axe.commons.text.isHumanInterpretable('I ✂ my hair');
assert.equal(actual, 1);
});

it('returns 1 when given string has both non BMP character, and emoji as part of the sentence', function () {
var actual = axe.commons.text.isHumanInterpretable(
const actual = axe.commons.text.isHumanInterpretable(
'I ✂ my hair, and I like 🏀'
);
assert.equal(actual, 1);
});

it('returns 0 when given string has only emoji', function () {
var actual = axe.commons.text.isHumanInterpretable('🏀🍔🍉🎅');
const actual = axe.commons.text.isHumanInterpretable('🏀🍔🍉🎅');
assert.equal(actual, 0);
});

it('returns 0 when given string has only non BNP characters', function () {
var actual = axe.commons.text.isHumanInterpretable('⌛👓');
const actual = axe.commons.text.isHumanInterpretable('⌛👓');
assert.equal(actual, 0);
});

it('returns 0 when given string has combination of only non BNP characters and emojis', function () {
var actual = axe.commons.text.isHumanInterpretable('⌛👓🏀🍔🍉🎅');
const actual = axe.commons.text.isHumanInterpretable('⌛👓🏀🍔🍉🎅');
assert.equal(actual, 0);
});

it('returns 1 when given string is a punctuated sentence', function () {
var actual = axe.commons.text.isHumanInterpretable(
const actual = axe.commons.text.isHumanInterpretable(
"I like football, but I prefer basketball; although I can't play either very well."
);
assert.equal(actual, 1);
});

it('returns 1 for a sentence without emoji or punctuations', function () {
var actual = axe.commons.text.isHumanInterpretable('Earth is round');
const actual = axe.commons.text.isHumanInterpretable('Earth is round');
assert.equal(actual, 1);
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@
<button id="incomplete9" aria-label="help">?</button>
<button id="incomplete10" aria-label="&#x1F354;">&#x1F354;</button>
<button id="incomplete11" aria-label="close">&#10060;</button>
<button id="incomplete12" aria-label="close">×</button>
<button id="incomplete13" aria-label="italic">i</button>
<button id="incomplete14" aria-label="bold">B</button>
<button id="incomplete15" aria-label="alphabetical sort">ABC</button>
<button id="incomplete16" aria-label="toggle capitalization">aA</button>
<button id="incomplete17" aria-label="CJK character"></button>

<!-- inapplicable -->
<a id="inapplicable1" aria-label="OK">Next</a>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@
["#incomplete8"],
["#incomplete9"],
["#incomplete10"],
["#incomplete11"]
["#incomplete11"],
["#incomplete12"],
["#incomplete13"],
["#incomplete14"],
["#incomplete15"],
["#incomplete16"],
["#incomplete17"]
]
}

0 comments on commit 56e139a

Please sign in to comment.