-
Notifications
You must be signed in to change notification settings - Fork 235
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(rule): tabindex should not be positive (#744)
- Loading branch information
1 parent
e7b2fa7
commit 43902f7
Showing
4 changed files
with
127 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import { ElementAst } from '@angular/compiler'; | ||
import { IRuleMetadata, RuleFailure, Rules } from 'tslint/lib'; | ||
import { SourceFile } from 'typescript/lib/typescript'; | ||
import { NgWalker } from './angular/ngWalker'; | ||
import { BasicTemplateAstVisitor } from './angular/templates/basicTemplateAstVisitor'; | ||
import { getAttributeValue } from './util/getAttributeValue'; | ||
|
||
export class Rule extends Rules.AbstractRule { | ||
static readonly metadata: IRuleMetadata = { | ||
description: 'Ensures that the tab index is not positive', | ||
options: null, | ||
optionsDescription: 'Not configurable.', | ||
rationale: 'positive values for tabidex attribute should be avoided because they mess up with the order of focus (AX_FOCUS_03)', | ||
ruleName: 'template-accessibility-tabindex-no-positive', | ||
type: 'functionality', | ||
typescriptOnly: true | ||
}; | ||
|
||
static readonly FAILURE_MESSAGE = 'Tabindex cannot be positive'; | ||
|
||
apply(sourceFile: SourceFile): RuleFailure[] { | ||
return this.applyWithWalker( | ||
new NgWalker(sourceFile, this.getOptions(), { | ||
templateVisitorCtrl: TemplateAccessibilityTabindexNoPositiveVisitor | ||
}) | ||
); | ||
} | ||
} | ||
|
||
class TemplateAccessibilityTabindexNoPositiveVisitor extends BasicTemplateAstVisitor { | ||
visitElement(ast: ElementAst, context: any): any { | ||
this.validateElement(ast); | ||
super.visitElement(ast, context); | ||
} | ||
|
||
private validateElement(element: ElementAst) { | ||
let tabIndexValue = getAttributeValue(element, 'tabindex'); | ||
if (tabIndexValue) { | ||
tabIndexValue = parseInt(tabIndexValue, 10); | ||
if (tabIndexValue > 0) { | ||
const { | ||
sourceSpan: { | ||
end: { offset: endOffset }, | ||
start: { offset: startOffset } | ||
} | ||
} = element; | ||
this.addFailureFromStartToEnd(startOffset, endOffset, Rule.FAILURE_MESSAGE); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { ElementAst } from '@angular/compiler'; | ||
|
||
export const getAttributeValue = (element: ElementAst, property: string) => { | ||
const attr = element.attrs.find(attr => attr.name === property); | ||
const input = element.inputs.find(input => input.name === property); | ||
if (attr) { | ||
return attr.value; | ||
} | ||
if (input) { | ||
return (<any>input.value).ast.value; | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
import { Rule } from '../src/templateAccessibilityTabindexNoPositiveRule'; | ||
import { assertAnnotated, assertSuccess } from './testHelper'; | ||
|
||
const { | ||
FAILURE_MESSAGE, | ||
metadata: { ruleName } | ||
} = Rule; | ||
|
||
describe(ruleName, () => { | ||
describe('failure', () => { | ||
it('should fail when tabindex attr is positive', () => { | ||
const source = ` | ||
@Component({ | ||
template: \` | ||
<div tabindex="5"></div> | ||
~~~~~~~~~~~~~~~~~~ | ||
\` | ||
}) | ||
class Bar {} | ||
`; | ||
assertAnnotated({ | ||
message: FAILURE_MESSAGE, | ||
ruleName, | ||
source | ||
}); | ||
}); | ||
|
||
it('should fail when tabindex input is positive', () => { | ||
const source = ` | ||
@Component({ | ||
template: \` | ||
<div [attr.tabindex]="1"></div> | ||
~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
\` | ||
}) | ||
class Bar {} | ||
`; | ||
assertAnnotated({ | ||
message: FAILURE_MESSAGE, | ||
ruleName, | ||
source | ||
}); | ||
}); | ||
}); | ||
|
||
describe('success', () => { | ||
it('should work with tab index is not positive', () => { | ||
const source = ` | ||
@Component({ | ||
template: \` | ||
<span tabindex="-1"></span> | ||
<span tabindex="0"></span> | ||
<span [attr.tabindex]="-1"></span> | ||
<span [attr.tabindex]="0"></span> | ||
<span [attr.tabindex]="tabIndex"></span> | ||
\` | ||
}) | ||
class Bar {} | ||
`; | ||
assertSuccess(ruleName, source); | ||
}); | ||
}); | ||
}); |