-
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): click event should be accompanied with key event (#761)
- Loading branch information
1 parent
77a5e32
commit b0b330f
Showing
3 changed files
with
101 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,55 @@ | ||
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'; | ||
|
||
export class Rule extends Rules.AbstractRule { | ||
static readonly metadata: IRuleMetadata = { | ||
description: 'Ensures that the click event is accompanied with at least one key event keyup, keydown or keypress', | ||
options: null, | ||
optionsDescription: 'Not configurable.', | ||
rationale: 'Keyboard is important for users with physical disabilities who cannot use mouse.', | ||
ruleName: 'template-click-events-have-key-events', | ||
type: 'functionality', | ||
typescriptOnly: true | ||
}; | ||
|
||
static readonly FAILURE_STRING = 'click must be accompanied by either keyup, keydown or keypress event for accessibility'; | ||
|
||
apply(sourceFile: SourceFile): RuleFailure[] { | ||
return this.applyWithWalker( | ||
new NgWalker(sourceFile, this.getOptions(), { | ||
templateVisitorCtrl: TemplateClickEventsHaveKeyEventsVisitor | ||
}) | ||
); | ||
} | ||
} | ||
|
||
class TemplateClickEventsHaveKeyEventsVisitor extends BasicTemplateAstVisitor { | ||
visitElement(el: ElementAst, context: any) { | ||
this.validateElement(el); | ||
super.visitElement(el, context); | ||
} | ||
|
||
private validateElement(el: ElementAst): void { | ||
const hasClick = el.outputs.some(output => output.name === 'click'); | ||
if (!hasClick) { | ||
return; | ||
} | ||
const hasKeyEvent = el.outputs.some(output => output.name === 'keyup' || output.name === 'keydown' || output.name === 'keypress'); | ||
|
||
if (hasKeyEvent) { | ||
return; | ||
} | ||
|
||
const { | ||
sourceSpan: { | ||
end: { offset: endOffset }, | ||
start: { offset: startOffset } | ||
} | ||
} = el; | ||
|
||
this.addFailureFromStartToEnd(startOffset, endOffset, Rule.FAILURE_STRING); | ||
} | ||
} |
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,45 @@ | ||
import { Rule } from '../src/templateClickEventsHaveKeyEventsRule'; | ||
import { assertAnnotated, assertSuccess } from './testHelper'; | ||
|
||
const { | ||
FAILURE_STRING, | ||
metadata: { ruleName } | ||
} = Rule; | ||
|
||
describe(ruleName, () => { | ||
describe('failure', () => { | ||
it('should fail when click is not accompanied with key events', () => { | ||
const source = ` | ||
@Component({ | ||
template: \` | ||
<div (click)="onClick()"></div> | ||
~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
\` | ||
}) | ||
class Bar {} | ||
`; | ||
assertAnnotated({ | ||
message: FAILURE_STRING, | ||
ruleName, | ||
source | ||
}); | ||
}); | ||
}); | ||
|
||
describe('success', () => { | ||
it('should work find when click events are associated with key events', () => { | ||
const source = ` | ||
@Component({ | ||
template: \` | ||
<div (click)="onClick()" (keyup)="onKeyup()"></div> | ||
<div (click)="onClick()" (keydown)="onKeyDown()"></div> | ||
<div (click)="onClick()" (keypress)="onKeyPress()"></div> | ||
<div (click)="onClick()" (keyup)="onKeyup()" (keydown)="onKeyDown()" (keypress)="onKeyPress()"></div> | ||
\` | ||
}) | ||
class Bar {} | ||
`; | ||
assertSuccess(ruleName, source); | ||
}); | ||
}); | ||
}); |