Skip to content
This repository has been archived by the owner on Jul 15, 2023. It is now read-only.

Commit

Permalink
#278 Implement react-a11y-no-onchange. (#543)
Browse files Browse the repository at this point in the history
* #278 Implement react-a11y-no-onchange.

* Fix description & options description.

* Remove redundant accessibility.

* Update csv.

* Fix failing test.
  • Loading branch information
sahalsaad authored and Josh Goldberg committed Oct 16, 2018
1 parent 4e70ba5 commit 43b6b3c
Show file tree
Hide file tree
Showing 6 changed files with 129 additions and 0 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ Rule Name | Description | Since
`react-a11y-img-has-alt` | Enforce that an `img` element contains the `alt` attribute or `role='presentation'` for a decorative image. All images must have `alt` text to convey their purpose and meaning to **screen reader users**. Besides, the `alt` attribute specifies an alternate text for an image, if the image cannot be displayed. This rule accepts as a parameter a string array for tag names other than img to also check. For example, if you use a custom tag named 'Image' then configure the rule with: `[true, ['Image']]`<br/>References:<br/>[Web Content Accessibility Guidelines 1.0](https://www.w3.org/TR/WCAG10/wai-pageauth.html#tech-text-equivalent)<br/>[ARIA Presentation Role](https://www.w3.org/TR/wai-aria/roles#presentation)<br/>[WCAG Rule 31: If an image has an alt or title attribute, it should not have a presentation role](http://oaa-accessibility.org/wcag20/rule/31/) | 2.0.11
`react-a11y-lang` | For accessibility of your website, HTML elements must have a lang attribute and the attribute must be a valid language code.<br/>References:<br/>* [H58: Using language attributes to identify changes in the human language](https://www.w3.org/TR/WCAG20-TECHS/H58.html)<br/>* [lang attribute must have a valid value](https://dequeuniversity.com/rules/axe/1.1/valid-lang)<br/>[List of ISO 639-1 codes](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes) | 2.0.11
`react-a11y-meta` | For accessibility of your website, HTML meta elements must not have http-equiv="refresh". | 2.0.11
`react-a11y-no-onchange` | For accessibility of your website, enforce usage of onBlur over onChange on select menus. | 5.2.3
`react-a11y-props` | For accessibility of your website, enforce all `aria-*` attributes are valid. Elements cannot use an invalid `aria-*` attribute. This rule will fail if it finds an `aria-*` attribute that is not listed in [WAI-ARIA states and properties](https://www.w3.org/WAI/PF/aria/states_and_properties#state_prop_values). | 2.0.11
`react-a11y-proptypes` | For accessibility of your website, enforce the type of aria state and property values are correct. | 2.0.11
`react-a11y-role-has-required-aria-props` | For accessibility of your website, elements with aria roles must have all required attributes according to the role. <br/>References:<br/>[ARIA Definition of Roles](https://www.w3.org/TR/wai-aria/roles#role_definitions)<br/>[WCAG Rule 90: Required properties and states should be defined](http://oaa-accessibility.org/wcag20/rule/90/)<br/>[WCAG Rule 91: Required properties and states must not be empty](http://oaa-accessibility.org/wcag20/rule/91/)<br/>| 2.0.11
Expand Down
1 change: 1 addition & 0 deletions recommended_ruleset.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ module.exports = {
"react-a11y-img-has-alt": true,
"react-a11y-lang": true,
"react-a11y-meta": true,
"react-a11y-no-onchange": true,
"react-a11y-props": true,
"react-a11y-proptypes": true,
"react-a11y-role": true,
Expand Down
64 changes: 64 additions & 0 deletions src/reactA11yNoOnchangeRule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import * as ts from 'typescript';
import * as Lint from 'tslint';

import {ErrorTolerantWalker} from './utils/ErrorTolerantWalker';
import {ExtendedMetadata} from './utils/ExtendedMetadata';
import {getJsxAttributesFromJsxElement} from './utils/JsxAttribute';

/**
* Implementation of the react-a11y-no-onchange rule.
*/
export class Rule extends Lint.Rules.AbstractRule {

public static metadata: ExtendedMetadata = {
ruleName: 'react-a11y-no-onchange',
type: 'functionality',
description: 'For accessibility of your website, enforce usage of onBlur over onChange on select menus.',
options: 'string[]',
optionsDescription: 'Additional tag names to validate.',
optionExamples: ['true', '[true, ["Select"]]'],
typescriptOnly: false,
issueClass: 'Non-SDL',
issueType: 'Warning',
severity: 'Important',
level: 'Opportunity for Excellence',
group: 'Accessibility'
};

public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
return sourceFile.languageVariant === ts.LanguageVariant.JSX ?
this.applyWithWalker(new ReactA11yNoOnchangeRuleWalker(sourceFile, this.getOptions())) :
[];
}
}

class ReactA11yNoOnchangeRuleWalker extends ErrorTolerantWalker {
protected visitJsxSelfClosingElement(node: ts.JsxSelfClosingElement): void {
this.checkJsxOpeningElement(node);
super.visitJsxSelfClosingElement(node);
}

protected visitJsxElement(node: ts.JsxElement): void {
this.checkJsxOpeningElement(node.openingElement);
super.visitJsxElement(node);
}

private checkJsxOpeningElement(node: ts.JsxOpeningLikeElement) {
const tagName: string = node.tagName.getText();
const options: any[] = this.getOptions();

const additionalTagNames: string[] = options.length > 0 ? options[0] : [];

const targetTagNames: string[] = ['select', ...additionalTagNames];

if (!tagName || targetTagNames.indexOf(tagName) === -1) {
return;
}

const attributes = getJsxAttributesFromJsxElement(node);
if (attributes.hasOwnProperty('onchange')) {
const errorMessage = `onChange event handler should not be used with the <${tagName}>. Please use onBlur instead.`;
this.addFailureAt(node.getStart(), node.getWidth(), errorMessage);
}
}
}
61 changes: 61 additions & 0 deletions src/tests/ReactA11yNoOnchangeRuleTests.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import {Utils} from '../utils/Utils';
import {TestHelper} from './TestHelper';

/**
* Unit tests.
*/
describe('reactA11yNoOnchangeRule', () : void => {
const ruleName : string = 'react-a11y-no-onchange';
const errorMessage = (tagName: string): string =>
`onChange event handler should not be used with the <${tagName}>. Please use onBlur instead.`;

it('should pass if select element attributes without onChange event', () : void => {
const script: string = `
import React = require('react');
const selectElement = <select />
const selectElementWithOnBlur = <select onBlur={} />`;

TestHelper.assertNoViolation(ruleName, script);
});

it('should fail if select element attributes contains onChange event', () : void => {
const script : string = `
import React = require('react');
const selectElementWithOnChange = <select onChange={} />\`;
`;

TestHelper.assertViolations(ruleName, script, [{
failure: errorMessage('select'),
name: Utils.absolutePath('file.tsx'),
ruleName,
ruleSeverity: "ERROR",
startPosition: {character: 47, line: 3}
}]);
});

it('should fail if additional tag name specified in options contains onChange event', () => {
const script : string = `
import React = require('react');
const selectElementWithOnChange = <Select onChange={} />
const selectElementWithOnChange = <select onChange={} />
`;

TestHelper.assertViolationsWithOptions(ruleName, ['Select'], script, [{
failure: errorMessage('Select'),
name: Utils.absolutePath('file.tsx'),
ruleName,
ruleSeverity: "ERROR",
startPosition: {character: 47, line: 3}
}, {
failure: errorMessage('select'),
name: Utils.absolutePath('file.tsx'),
ruleName,
ruleSeverity: 'ERROR',
startPosition: {
'character': 47,
'line': 4
}
}]);
});

});
1 change: 1 addition & 0 deletions tslint-warnings.csv
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ react-a11y-event-has-role,Elements with event handlers must have role attribute.
react-a11y-image-button-has-alt,Enforce that inputs element with type="image" must have alt attribute.,TSLINTVBN64L,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,,
react-a11y-img-has-alt,"Enforce that an img element contains the non-empty alt attribute. For decorative images, using empty alt attribute and role="presentation".",TSLINT1OM69KS,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,,
react-a11y-lang,"For accessibility of your website, html elements must have a valid lang attribute.",TSLINTQ046RM,tslint,Non-SDL,Warning,Low,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,,
react-a11y-no-onchange,"For accessibility of your website, enforce usage of onBlur over onChange on select menus.",TSLINTNO0TDD,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,,
react-a11y-props,Enforce all `aria-*` attributes are valid. Elements cannot use an invalid `aria-*` attribute.,TSLINT1682S78,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,,
react-a11y-proptypes,Enforce ARIA state and property values are valid.,TSLINT1DLB1JE,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,,
react-a11y-role,"Elements with aria roles must use a **valid**, **non-abstract** aria role.",TSLINTQ0A2FU,tslint,Non-SDL,Warning,Important,Opportunity for Excellence,See description on the tslint or tslint-microsoft-contrib website,TSLint Procedure,,
Expand Down
1 change: 1 addition & 0 deletions tslint.json
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,7 @@
"react-a11y-img-has-alt": true,
"react-a11y-lang": true,
"react-a11y-meta": true,
"react-a11y-no-onchange": true,
"react-a11y-props": true,
"react-a11y-proptypes": true,
"react-a11y-role": true,
Expand Down

0 comments on commit 43b6b3c

Please sign in to comment.