Skip to content

Commit 551ba24

Browse files
committed
fix(aria-valid-attr-value): aria-controls & aria-haspopup incomplete
if an element has both aria-controls and aria-haspopup mark it incomplete as we are unsure if the DOM element will be added dynamically later Refs: #4363
1 parent 75b0c11 commit 551ba24

File tree

4 files changed

+26
-4
lines changed

4 files changed

+26
-4
lines changed

lib/checks/aria/aria-valid-attr-value-evaluate.js

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,12 +36,23 @@ export default function ariaValidAttrValueEvaluate(node, options, virtualNode) {
3636

3737
const preChecks = {
3838
// aria-controls should only check if element exists if the element
39-
// doesn't have aria-expanded=false or aria-selected=false (tabs)
39+
// doesn't have aria-expanded=false, aria-selected=false (tabs),
40+
// or aria-haspopup (may load later)
4041
// @see https://github.com/dequelabs/axe-core/issues/1463
42+
// @see https://github.com/dequelabs/axe-core/issues/4363
4143
'aria-controls': () => {
44+
const hasPopup =
45+
['false', null].includes(virtualNode.attr('aria-haspopup')) === false;
46+
47+
if (hasPopup) {
48+
needsReview = `aria-controls="${virtualNode.attr('aria-controls')}"`;
49+
messageKey = 'controlsWithinPopup';
50+
}
51+
4252
return (
4353
virtualNode.attr('aria-expanded') !== 'false' &&
44-
virtualNode.attr('aria-selected') !== 'false'
54+
virtualNode.attr('aria-selected') !== 'false' &&
55+
hasPopup === false
4556
);
4657
},
4758
// aria-current should mark as needs review if any value is used that is

lib/checks/aria/aria-valid-attr-value.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
"noIdShadow": "ARIA attribute element ID does not exist on the page or is a descendant of a different shadow DOM tree: ${data.needsReview}",
1616
"ariaCurrent": "ARIA attribute value is invalid and will be treated as \"aria-current=true\": ${data.needsReview}",
1717
"idrefs": "Unable to determine if ARIA attribute element ID exists on the page: ${data.needsReview}",
18-
"empty": "ARIA attribute value is ignored while empty: ${data.needsReview}"
18+
"empty": "ARIA attribute value is ignored while empty: ${data.needsReview}",
19+
"controlsWithinPopup": "Unable to determine if aria-controls referenced ID exists on the page while using aria-haspopup: ${data.needsReview}"
1920
}
2021
}
2122
}

locales/_template.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -538,7 +538,8 @@
538538
"noIdShadow": "ARIA attribute element ID does not exist on the page or is a descendant of a different shadow DOM tree: ${data.needsReview}",
539539
"ariaCurrent": "ARIA attribute value is invalid and will be treated as \"aria-current=true\": ${data.needsReview}",
540540
"idrefs": "Unable to determine if ARIA attribute element ID exists on the page: ${data.needsReview}",
541-
"empty": "ARIA attribute value is ignored while empty: ${data.needsReview}"
541+
"empty": "ARIA attribute value is ignored while empty: ${data.needsReview}",
542+
"controlsWithinPopup": "Unable to determine if aria-controls referenced ID exists on the page while using aria-haspopup: ${data.needsReview}"
542543
}
543544
},
544545
"aria-valid-attr": {

test/checks/aria/valid-attr-value.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,15 @@ describe('aria-valid-attr-value', function () {
110110
assert.isFalse(validAttrValueCheck.call(checkContext, null, null, vNode));
111111
});
112112

113+
it('should return undefined on aria-controls with aria-haspopup as we cannot determine if it is in the DOM later', function () {
114+
var vNode = queryFixture(
115+
'<button id="target" aria-controls="test" aria-haspopup="true">Button</button>'
116+
);
117+
assert.isUndefined(
118+
validAttrValueCheck.call(checkContext, null, null, vNode)
119+
);
120+
});
121+
113122
it('should pass on aria-owns and aria-expanded=false when the element is not in the DOM', function () {
114123
var vNode = queryFixture(
115124
'<button id="target" aria-owns="test" aria-expanded="false">Button</button>'

0 commit comments

Comments
 (0)