Skip to content

Commit 1315f8e

Browse files
authored
feat(label,select-name): allow placeholder to pass label rule, add select-name rule (#2448)
* feat(label,select-name): allow placeholder to pass label rule, add select-name rule * add role none/presentation check * fix
1 parent 750a95b commit 1315f8e

File tree

16 files changed

+355
-43
lines changed

16 files changed

+355
-43
lines changed

doc/rule-descriptions.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
| [object-alt](https://dequeuniversity.com/rules/axe/4.0/object-alt?application=RuleDescription) | Ensures <object> elements have alternate text | Serious | cat.text-alternatives, wcag2a, wcag111, section508, section508.22.a | failure, needs review |
5454
| [role-img-alt](https://dequeuniversity.com/rules/axe/4.0/role-img-alt?application=RuleDescription) | Ensures [role='img'] elements have alternate text | Serious | cat.text-alternatives, wcag2a, wcag111, section508, section508.22.a | failure, needs review |
5555
| [scrollable-region-focusable](https://dequeuniversity.com/rules/axe/4.0/scrollable-region-focusable?application=RuleDescription) | Elements that have scrollable content should be accessible by keyboard | Moderate | wcag2a, wcag211 | failure |
56+
| [select-name](https://dequeuniversity.com/rules/axe/4.0/select-name?application=RuleDescription) | Ensures select element has an accessible name | Minor, Critical | cat.forms, wcag2a, wcag412, wcag131, section508, section508.22.n | failure, needs review |
5657
| [server-side-image-map](https://dequeuniversity.com/rules/axe/4.0/server-side-image-map?application=RuleDescription) | Ensures that server-side image maps are not used | Minor | cat.text-alternatives, wcag2a, wcag211, section508, section508.22.f | needs review |
5758
| [svg-img-alt](https://dequeuniversity.com/rules/axe/4.0/svg-img-alt?application=RuleDescription) | Ensures svg elements with an img, graphics-document or graphics-symbol role have an accessible text | Serious | cat.text-alternatives, wcag2a, wcag111, section508, section508.22.a | failure, needs review |
5859
| [td-headers-attr](https://dequeuniversity.com/rules/axe/4.0/td-headers-attr?application=RuleDescription) | Ensure that each cell in a table using the headers refers to another cell in that table | Serious | cat.tables, wcag2a, wcag131, section508, section508.22.g | failure, needs review |
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
{
2+
"id": "non-empty-placeholder",
3+
"evaluate": "attr-non-space-content-evaluate",
4+
"options": {
5+
"attribute": "placeholder"
6+
},
7+
"metadata": {
8+
"impact": "serious",
9+
"messages": {
10+
"pass": "Element has a placeholder attribute",
11+
"fail": "Element has no placeholder attribute or the placeholder attribute is empty"
12+
}
13+
}
14+
}

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

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,18 @@ const nativeTextMethods = {
9393
*/
9494
singleSpace: function singleSpace() {
9595
return ' ';
96-
}
96+
},
97+
98+
/**
99+
* Return the placeholder text
100+
* @param {VirtualNode} element
101+
* @return {String} placeholder text
102+
*/
103+
placeholderText: attrText.bind(null, 'placeholder')
97104
};
98105

99-
function attrText(attr, { actualNode }) {
100-
return actualNode.getAttribute(attr) || '';
106+
function attrText(attr, vNode) {
107+
return vNode.attr(attr) || '';
101108
}
102109

103110
/**

lib/rules/label.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"id": "label",
3-
"selector": "input, select, textarea",
3+
"selector": "input, textarea",
44
"matches": "label-matches",
55
"tags": [
66
"cat.forms",
@@ -21,6 +21,7 @@
2121
"implicit-label",
2222
"explicit-label",
2323
"non-empty-title",
24+
"non-empty-placeholder",
2425
"role-none",
2526
"role-presentation"
2627
],

lib/rules/select-name.json

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"id": "select-name",
3+
"selector": "select",
4+
"tags": [
5+
"cat.forms",
6+
"wcag2a",
7+
"wcag412",
8+
"wcag131",
9+
"section508",
10+
"section508.22.n"
11+
],
12+
"metadata": {
13+
"description": "Ensures select element has an accessible name",
14+
"help": "Select element must have and accessible name"
15+
},
16+
"all": [],
17+
"any": [
18+
"aria-label",
19+
"aria-labelledby",
20+
"implicit-label",
21+
"explicit-label",
22+
"non-empty-title",
23+
"role-none",
24+
"role-presentation"
25+
],
26+
"none": ["help-same-as-label", "hidden-explicit-label"]
27+
}

lib/standards/html-elms.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -502,9 +502,9 @@ const htmlElms = {
502502
implicitAttrs: {
503503
'aria-valuenow': ''
504504
},
505-
// 5.1 input type="text", input type="password", input type="search", input type="tel", input type="url" and textarea Element
505+
// 5.1 input type="text", input type="password", input type="search", input type="tel", input type="url"
506506
// 5.7 Other Form Elements
507-
namingMethods: ['labelText']
507+
namingMethods: ['labelText', 'placeholderText']
508508
}
509509
}
510510
},
@@ -840,7 +840,8 @@ const htmlElms = {
840840
'aria-valuenow': '',
841841
'aria-multiline': 'true'
842842
},
843-
namingMethods: ['labelText']
843+
// 5.1 textarea
844+
namingMethods: ['labelText', 'placeholderText']
844845
},
845846
tfoot: {
846847
allowedRoles: true
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
describe('non-empty-placeholder', function() {
2+
'use strict';
3+
4+
var fixture = document.getElementById('fixture');
5+
var checkSetup = axe.testUtils.checkSetup;
6+
var checkEvaluate = axe.testUtils.getCheckEvaluate('non-empty-placeholder');
7+
8+
afterEach(function() {
9+
fixture.innerHTML = '';
10+
});
11+
12+
it('should return true if a placeholder is present', function() {
13+
var params = checkSetup('<input id="target" placeholder="woohoo" />');
14+
15+
assert.isTrue(checkEvaluate.apply(null, params));
16+
});
17+
18+
it('should return false if a placeholder is not present', function() {
19+
var params = checkSetup('<input id="target" />');
20+
21+
assert.isFalse(checkEvaluate.apply(null, params));
22+
});
23+
24+
it('should return false if a placeholder is present, but empty', function() {
25+
var params = checkSetup('<input id="target" placeholder=" " />');
26+
27+
assert.isFalse(checkEvaluate.apply(null, params));
28+
});
29+
30+
it('should collapse whitespace', function() {
31+
var params = checkSetup(
32+
'<input id="target" placeholder=" \t \n \r \t \t\r\n " />'
33+
);
34+
35+
assert.isFalse(checkEvaluate.apply(null, params));
36+
});
37+
});

test/commons/text/accessible-text.js

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1026,8 +1026,7 @@ describe('text.accessibleTextVirtual', function() {
10261026
});
10271027
});
10281028

1029-
// not implemented yet, doesn't work accross ATs
1030-
it.skip('should find a placeholder attribute', function() {
1029+
it('should find a placeholder attribute', function() {
10311030
types.forEach(function(type) {
10321031
var t = type ? ' type="' + type + '"' : '';
10331032
fixture.innerHTML = '<input' + t + ' placeholder="Hello World">';
@@ -1118,8 +1117,7 @@ describe('text.accessibleTextVirtual', function() {
11181117
);
11191118
});
11201119

1121-
// not implemented yet, doesn't work accross ATs
1122-
it.skip('should find a placeholder attribute', function() {
1120+
it('should find a placeholder attribute', function() {
11231121
fixture.innerHTML = '<textarea placeholder="Hello World"></textarea>';
11241122
axe.testUtils.flatTreeSetup(fixture);
11251123

test/commons/text/native-text-methods.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,4 +184,19 @@ describe('text.nativeTextMethods', function() {
184184
assert.equal(singleSpace(), ' ');
185185
});
186186
});
187+
188+
describe('placeholderText', function() {
189+
var placeholderText = nativeTextMethods.placeholderText;
190+
it('returns the placeholder attribute of actualNode', function() {
191+
fixtureSetup('<input placeholder="foo" />');
192+
var input = axe.utils.querySelectorAll(axe._tree[0], 'input')[0];
193+
assert.equal(placeholderText(input), 'foo');
194+
});
195+
196+
it('returns `` when there is no placeholder', function() {
197+
fixtureSetup('<input />');
198+
var input = axe.utils.querySelectorAll(axe._tree[0], 'input')[0];
199+
assert.equal(placeholderText(input), '');
200+
});
201+
});
187202
});

test/integration/rules/label/label.html

Lines changed: 3 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,42 +5,26 @@
55
<input type="submit" id="na4" />
66
<input type="reset" id="na5" />
77
<input type="text" id="fail1" />
8-
<select id="fail2"></select>
98
<textarea id="fail3"></textarea>
109
<input type="text" aria-label="label" id="pass1" />
11-
<select aria-label="label" id="pass2"></select>
1210
<textarea aria-label="label" id="pass3"></textarea>
1311
<input type="text" aria-labelledby="label" id="pass4" />
14-
<select aria-labelledby="label" id="pass5"></select>
1512
<textarea aria-labelledby="label" id="pass6"></textarea>
1613
<div id="label">Label</div>
1714
<label>Label <input type="text" id="pass7"/></label>
18-
<label
19-
>Label
20-
<select id="pass8"></select
21-
></label>
2215
<label>Label <textarea id="pass9"></textarea></label>
2316

2417
<label><input type="text" id="fail4"/></label>
25-
<label><select id="fail5"></select></label>
2618
<label><textarea id="fail6"></textarea></label>
2719
<label for="fail7"></label><input type="text" id="fail7" />
28-
<label for="fail8"></label
29-
><select id="fail8"></select>
3020
<label for="fail9"></label><textarea id="fail9"></textarea>
31-
<label for="fail10"
32-
><select id="fail10">
33-
<option>Thing</option>
34-
</select></label
35-
>
21+
3622
<label for="fail11" style="display: none;">Text</label
3723
><input type="text" id="fail11" /> <label for="pass10">Label</label
38-
><input type="text" id="pass10" /> <label for="pass11">Label</label
39-
><select id="pass11"></select>
40-
<label for="pass12">Label</label><textarea id="pass12"></textarea>
24+
><input type="text" id="pass10" /> <label for="pass12">Label</label
25+
><textarea id="pass12"></textarea>
4126

4227
<input id="pass13" title="Label" />
43-
<select id="pass14" title="Label"></select>
4428
<textarea id="pass15" title="Label"></textarea>
4529

4630
<label

0 commit comments

Comments
 (0)