diff --git a/src/reactA11yAnchorsRule.ts b/src/reactA11yAnchorsRule.ts index 5b861dea3..7a5dfddc1 100644 --- a/src/reactA11yAnchorsRule.ts +++ b/src/reactA11yAnchorsRule.ts @@ -5,11 +5,19 @@ import {ErrorTolerantWalker} from './utils/ErrorTolerantWalker'; import {ExtendedMetadata} from './utils/ExtendedMetadata'; import {SyntaxKind} from './utils/SyntaxKind'; import {Utils} from './utils/Utils'; +import {getImplicitRole} from './utils/getImplicitRole'; +import { + getJsxAttributesFromJsxElement, + getStringLiteral +} from './utils/JsxAttribute'; + +const ROLE_STRING: string = 'role'; const NO_HASH_FAILURE_STRING: string = 'Do not use # as anchor href.'; const LINK_TEXT_TOO_SHORT_FAILURE_STRING: string = - 'Link text should be at least 4 characters long.'; + 'Link text should be at least 4 characters long. If you are not using ' + + 'element as anchor, please specify explicit role, e.g. role=\'button\''; const UNIQUE_ALT_FAILURE_STRING: string = 'Links with images and text content, the alt attribute should be unique to the text content or empty.'; const SAME_HREF_SAME_TEXT_FAILURE_STRING: string = @@ -117,7 +125,7 @@ class ReactA11yAnchorsRuleWalker extends ErrorTolerantWalker { this.addFailure(this.createFailure(anchorInfo.start, anchorInfo.width, NO_HASH_FAILURE_STRING)); } - if (!anchorInfo.text || anchorInfo.text.length < 4) { + if (this.imageRole(openingElement) === 'link' && (!anchorInfo.text || anchorInfo.text.length < 4)) { this.addFailure(this.createFailure(anchorInfo.start, anchorInfo.width, LINK_TEXT_TOO_SHORT_FAILURE_STRING)); } @@ -202,6 +210,14 @@ class ReactA11yAnchorsRuleWalker extends ErrorTolerantWalker { return altText; } + + private imageRole(root: ts.Node): string { + const attributesInElement: { [propName: string]: ts.JsxAttribute } = getJsxAttributesFromJsxElement(root); + const roleProp: ts.JsxAttribute = attributesInElement[ROLE_STRING]; + + // If role attribute is specified, get the role value. Otherwise get the implicit role from tag name. + return roleProp ? getStringLiteral(roleProp) : getImplicitRole(root); + } } class AnchorInfo { diff --git a/src/tests/ReactA11yAnchorsRuleTests.ts b/src/tests/ReactA11yAnchorsRuleTests.ts index e1d8681ee..def995138 100644 --- a/src/tests/ReactA11yAnchorsRuleTests.ts +++ b/src/tests/ReactA11yAnchorsRuleTests.ts @@ -35,7 +35,8 @@ describe('reactA11yAnchorsRule', () : void => { TestHelper.assertViolations(ruleName, script, [ { - "failure": "Link text should be at least 4 characters long.", + "failure": 'Link text should be at least 4 characters long. If you are not using ' + + 'element as anchor, please specify explicit role, e.g. role=\'button\'', "name": "file.tsx", "ruleName": "react-a11y-anchors", "startPosition": { "character": 28, "line": 3 } @@ -73,7 +74,8 @@ describe('reactA11yAnchorsRule', () : void => { "startPosition": { "character": 28, "line": 3 } }, { - "failure": "Link text should be at least 4 characters long.", + "failure": 'Link text should be at least 4 characters long. If you are not using ' + + 'element as anchor, please specify explicit role, e.g. role=\'button\'', "name": "file.tsx", "ruleName": "react-a11y-anchors", "startPosition": { "character": 28, "line": 3 } @@ -93,6 +95,18 @@ describe('reactA11yAnchorsRule', () : void => { TestHelper.assertViolations(ruleName, script, []); }); + it('should pass when role is not link and length of text less than 4', () => { + // Anchor without 'href' attribute has no corresponding role. + const script: string = ` + import React = require('react'); + const anchor1 = add; + const anchor2 = ; + const anchor3 = someAlt; + `; + + TestHelper.assertNoViolation(ruleName, script); + }); + it('should fail when length of text less than 4', (): void => { const script: string = ` import React = require('react'); @@ -102,13 +116,15 @@ describe('reactA11yAnchorsRule', () : void => { TestHelper.assertViolations(ruleName, script, [ { - "failure": "Link text should be at least 4 characters long.", + "failure": 'Link text should be at least 4 characters long. If you are not using ' + + 'element as anchor, please specify explicit role, e.g. role=\'button\'', "name": "file.tsx", "ruleName": "react-a11y-anchors", "startPosition": { "character": 33, "line": 3 } }, { - "failure": "Link text should be at least 4 characters long.", + "failure": 'Link text should be at least 4 characters long. If you are not using ' + + 'element as anchor, please specify explicit role, e.g. role=\'button\'', "name": "file.tsx", "ruleName": "react-a11y-anchors", "startPosition": { "character": 33, "line": 4 }