Skip to content

Commit cbf6a8d

Browse files
authored
Release 5.10.0 (#166)
1 parent b275c8b commit cbf6a8d

File tree

9 files changed

+132
-7
lines changed

9 files changed

+132
-7
lines changed

build/tasks/validate.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,7 @@ const categories = [
331331
'sensory-and-visual-cues',
332332
'structure',
333333
'tables',
334+
'labels',
334335
'text-alternatives',
335336
'time-and-media',
336337
'images'

lib/checks/label/label-content-name-mismatch-evaluate.js

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,42 @@ import {
55
sanitize,
66
removeUnicode
77
} from '../../commons/text';
8+
import stem from 'wink-porter2-stemmer';
9+
10+
const threshold = 0.75;
11+
12+
function cleanText(str) {
13+
return str
14+
?.toLowerCase()
15+
.normalize('NFKC')
16+
.replace(/[\u200B-\u200D\u2060\uFEFF]/g, '')
17+
.trim();
18+
}
19+
20+
function replaceSynonyms(text) {
21+
const synonymMap = {
22+
'&': 'and'
23+
};
24+
return text
25+
.split(/[^\p{L}\p{N}]+/u)
26+
.map(word => synonymMap[word] || word)
27+
.join(' ');
28+
}
29+
30+
function stringStemmer(str) {
31+
return replaceSynonyms(str)
32+
.split(/[^\p{L}\p{N}]+/u)
33+
.filter(Boolean)
34+
.map(word => {
35+
const w = cleanText(word).replace(/[^\p{L}\p{N}]/gu, '');
36+
try {
37+
return stem(w);
38+
} catch (err) {
39+
return w;
40+
}
41+
})
42+
.join(' ');
43+
}
844

945
/**
1046
* Check if a given text exists in another
@@ -14,12 +50,45 @@ import {
1450
* @returns {Boolean}
1551
*/
1652
function isStringContained(compare, compareWith) {
53+
compare = stringStemmer(compare);
54+
compareWith = stringStemmer(compareWith);
55+
1756
const curatedCompareWith = curateString(compareWith);
1857
const curatedCompare = curateString(compare);
1958
if (!curatedCompareWith || !curatedCompare) {
2059
return false;
2160
}
22-
return curatedCompareWith.includes(curatedCompare);
61+
const res = curatedCompareWith.includes(curatedCompare);
62+
if (res) {
63+
return res;
64+
}
65+
66+
const tokensA = compare.split(/[^\p{L}\p{N}]+/u);
67+
const tokensB = compareWith.split(/[^\p{L}\p{N}]+/u);
68+
const freqA = {},
69+
freqB = {};
70+
tokensA.forEach(word => {
71+
freqA[word] = (freqA[word] || 0) + 1;
72+
});
73+
tokensB.forEach(word => {
74+
freqB[word] = (freqB[word] || 0) + 1;
75+
});
76+
77+
let dot = 0,
78+
magA = 0,
79+
magB = 0;
80+
const allTerms = new Set([...Object.keys(freqA), ...Object.keys(freqB)]);
81+
allTerms.forEach(term => {
82+
const a = freqA[term] || 0;
83+
const b = freqB[term] || 0;
84+
dot += a * b;
85+
magA += a * a;
86+
magB += b * b;
87+
});
88+
89+
const similarity =
90+
magA && magB ? dot / (Math.sqrt(magA) * Math.sqrt(magB)) : 0;
91+
return similarity >= threshold; // comparision with threshold as 75%
2392
}
2493

2594
/**
@@ -32,7 +101,8 @@ function curateString(str) {
32101
const noUnicodeStr = removeUnicode(str, {
33102
emoji: true,
34103
nonBmp: true,
35-
punctuations: true
104+
punctuations: true,
105+
whitespace: true
36106
});
37107
return sanitize(noUnicodeStr);
38108
}
@@ -52,9 +122,11 @@ function labelContentNameMismatchEvaluate(node, options, virtualNode) {
52122
subtreeDescendant: true,
53123
ignoreIconLigature: true,
54124
pixelThreshold,
55-
occurrenceThreshold
125+
occurrenceThreshold,
126+
ignoreNativeTextAlternative: true // To Skip for nativeTextAlternative
56127
})
57128
).toLowerCase();
129+
58130
if (!visibleText) {
59131
return true;
60132
}

lib/commons/text/native-text-alternative.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ import nativeTextMethods from './native-text-methods';
1010
* @return {String} Accessible text
1111
*/
1212
export default function nativeTextAlternative(virtualNode, context = {}) {
13+
if (context.ignoreNativeTextAlternative) {
14+
return '';
15+
}
16+
1317
const { actualNode } = virtualNode;
1418
if (
1519
virtualNode.props.nodeType !== 1 ||

lib/commons/text/remove-unicode.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ import emojiRegexText from 'emoji-regex';
2020
* @returns {String}
2121
*/
2222
function removeUnicode(str, options) {
23-
const { emoji, nonBmp, punctuations } = options;
23+
const { emoji, nonBmp, punctuations, whitespace } = options;
2424

2525
if (emoji) {
2626
str = str.replace(emojiRegexText(), '');
@@ -34,6 +34,9 @@ function removeUnicode(str, options) {
3434
if (punctuations) {
3535
str = str.replace(getPunctuationRegExp(), '');
3636
}
37+
if (whitespace) {
38+
str = str.replace(/\s+/g, '');
39+
}
3740

3841
return str;
3942
}

lib/commons/text/subtree-text.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,19 @@ function subtreeText(virtualNode, context = {}) {
5959

6060
const phrasingElements = getElementsByContentType('phrasing').concat(['#text']);
6161

62+
function skipByInlineOverflow(virtualNode) {
63+
const computedStyleOverflow = virtualNode._cache.computedStyle_overflow;
64+
if (computedStyleOverflow && computedStyleOverflow === 'hidden') {
65+
return true;
66+
}
67+
return false;
68+
}
69+
6270
function appendAccessibleText(contentText, virtualNode, context) {
71+
if (skipByInlineOverflow(virtualNode)) {
72+
return contentText;
73+
}
74+
6375
const nodeName = virtualNode.props.nodeName;
6476
let contentTextAdd = accessibleTextVirtual(virtualNode, context);
6577
if (!contentTextAdd) {

lib/rules/autocomplete-valid.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
],
1414
"actIds": ["73f2c2"],
1515
"metadata": {
16-
"description": "Ensure that the necessary form fields use the autocomplete attribute with a valid input.",
16+
"description": "Ensures that the necessary form fields use the autocomplete attribute with a valid input.",
1717
"help": "Autocomplete attribute must have a valid value"
1818
},
1919
"all": ["autocomplete-valid"],

lib/rules/heading-order-bp.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"id": "heading-order-bp",
3+
"impact": "moderate",
4+
"selector": "h1, h2, h3, h4, h5, h6, [role=heading]",
5+
"matches": "heading-matches",
6+
"tags": [
7+
"cat.structure",
8+
"best-practice",
9+
"a11y-engine",
10+
"a11y-engine-experimental"
11+
],
12+
"metadata": {
13+
"description": "Ensures the order of headings is semantically correct",
14+
"help": "Heading levels should only increase by one"
15+
},
16+
"all": [],
17+
"any": ["heading-order"],
18+
"none": []
19+
}

package-lock.json

Lines changed: 14 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,8 @@
178178
"typescript": "^5.2.2",
179179
"uglify-js": "^3.17.4",
180180
"wcag-act-rules": "github:w3c/wcag-act-rules#dc90495a5533d326b300ee5a9487afdfc6d493c0",
181-
"weakmap-polyfill": "^2.0.4"
181+
"weakmap-polyfill": "^2.0.4",
182+
"wink-porter2-stemmer": "^2.0.1"
182183
},
183184
"lint-staged": {
184185
"*.{md,json,ts,html}": [

0 commit comments

Comments
 (0)