Skip to content

Commit bb84abc

Browse files
mattxwangljharb
authored andcommitted
[New] anchor-ambiguous-text, getAccessibleChildText: Implements check for alt tags on <img /> elements
1 parent c9687cc commit bb84abc

File tree

4 files changed

+47
-1
lines changed

4 files changed

+47
-1
lines changed

__tests__/src/rules/anchor-ambiguous-text-test.js

+13
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ ruleTester.run('anchor-ambiguous-text', rule, {
3939
{ code: '<a>${here}</a>;' },
4040
{ code: '<a aria-label="tutorial on using eslint-plugin-jsx-a11y">click here</a>;' },
4141
{ code: '<a><span aria-label="tutorial on using eslint-plugin-jsx-a11y">click here</span></a>;' },
42+
{ code: '<a><img alt="documentation" /></a>;' },
4243
{
4344
code: '<a>click here</a>',
4445
options: [{
@@ -49,6 +50,10 @@ ruleTester.run('anchor-ambiguous-text', rule, {
4950
code: '<Link>documentation</Link>;',
5051
settings: { 'jsx-a11y': { components: { Link: 'a' } } },
5152
},
53+
{
54+
code: '<a><Image alt="documentation" /></a>;',
55+
settings: { 'jsx-a11y': { components: { Image: 'img' } } },
56+
},
5257
{
5358
code: '<Link>${here}</Link>;',
5459
settings: { 'jsx-a11y': { components: { Link: 'a' } } },
@@ -80,12 +85,20 @@ ruleTester.run('anchor-ambiguous-text', rule, {
8085
{ code: '<a><span> click </span> here</a>;', errors: [expectedError] },
8186
{ code: '<a><span aria-hidden>more text</span>learn more</a>;', errors: [expectedError] },
8287
{ code: '<a><span aria-hidden="true">more text</span>learn more</a>;', errors: [expectedError] },
88+
{ code: '<a><img alt="click here"/></a>;', errors: [expectedError] },
89+
{ code: '<a alt="tutorial on using eslint-plugin-jsx-a11y">click here</a>;', errors: [expectedError] },
90+
{ code: '<a><span alt="tutorial on using eslint-plugin-jsx-a11y">click here</span></a>;', errors: [expectedError] },
8391
{ code: '<a><CustomElement>click</CustomElement> here</a>;', errors: [expectedError] },
8492
{
8593
code: '<Link>here</Link>',
8694
errors: [expectedError],
8795
settings: { 'jsx-a11y': { components: { Link: 'a' } } },
8896
},
97+
{
98+
code: '<a><Image alt="click here" /></a>',
99+
errors: [expectedError],
100+
settings: { 'jsx-a11y': { components: { Image: 'img' } } },
101+
},
89102
{
90103
code: '<a>a disallowed word</a>',
91104
errors: [expectedErrorGenerator(['a disallowed word'])],

__tests__/src/util/getAccessibleChildText-test.js

+21
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,27 @@ describe('getAccessibleChildText', () => {
3535
), elementType)).toBe('bar');
3636
});
3737

38+
it('returns alt text for img child', () => {
39+
expect(getAccessibleChildText(JSXElementMock(
40+
'a',
41+
[],
42+
[JSXElementMock('img', [
43+
JSXAttributeMock('src', 'some/path'),
44+
JSXAttributeMock('alt', 'a sensible label'),
45+
])],
46+
), elementType)).toBe('a sensible label');
47+
});
48+
49+
it('returns blank when alt tag is used on arbitrary element', () => {
50+
expect(getAccessibleChildText(JSXElementMock(
51+
'a',
52+
[],
53+
[JSXElementMock('span', [
54+
JSXAttributeMock('alt', 'a sensible label'),
55+
])],
56+
), elementType)).toBe('');
57+
});
58+
3859
it('returns literal value for JSXText child', () => {
3960
expect(getAccessibleChildText(JSXElementMock(
4061
'a',

docs/rules/anchor-ambiguous-text.md

+9-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,13 @@ The `words` option allows users to modify the strings that can be checked for in
2222
const DEFAULT_AMBIGUOUS_WORDS = ['click here', 'here', 'link', 'a link', 'learn more'];
2323
```
2424

25-
If an element has the `aria-label` property, its value is used instead of the inner text. Note that the rule still disallows ambiguous `aria-label`s. This rule also skips over elements with `aria-hidden="true"`.
25+
The logic to calculate the inner text of an anchor is as follows:
26+
27+
- if an element has the `aria-label` property, its value is used instead of the inner text
28+
- if an element has `aria-hidden="true`, it is skipped over
29+
- if an element is `<img />` or configured to be interpreted like one, its `alt` value is used as its inner text
30+
31+
Note that this rule still disallows ambiguous `aria-label` or `alt` values.
2632

2733
Note that this rule is case-insensitive and trims whitespace. It only looks for **exact matches**.
2834

@@ -46,6 +52,8 @@ Note that this rule is case-insensitive and trims whitespace. It only looks for
4652
<a><i></i>a link</a>
4753
<a><span aria-hidden="true">more text</span>learn more</a> // skips over elements with aria-hidden=true
4854
<a aria-label="click here">something</a> // the aria-label here is inaccessible
55+
<a><img alt="click here"/></a> // the alt tag is still ambiguous
56+
<a alt="tutorial on using eslint-plugin-jsx-a11y">click here</a> // the alt tag is only parsed on img
4957
```
5058

5159
## Accessibility guidelines

src/util/getAccessibleChildText.js

+4
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ export default function getAccessibleChildText(node: JSXElement, elementType: (J
2929
// early escape-hatch when aria-label is applied
3030
if (ariaLabel) return standardizeSpaceAndCase(ariaLabel);
3131

32+
// early-return if alt prop exists and is an image
33+
const altTag = getLiteralPropValue(getProp(node.openingElement.attributes, 'alt'));
34+
if (elementType(node.openingElement) === 'img' && altTag) return standardizeSpaceAndCase(altTag);
35+
3236
// skip if aria-hidden is true
3337
if (
3438
isHiddenFromScreenReader(

0 commit comments

Comments
 (0)