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

Commit

Permalink
[Issue #194] react-a11y-lang - valid lang value
Browse files Browse the repository at this point in the history
Values that is passed to lang attribute must be a valid ISO language
code.
  • Loading branch information
danielmanesku committed Aug 31, 2016
1 parent 23645be commit 41b9565
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 8 deletions.
36 changes: 30 additions & 6 deletions src/reactA11yLangRule.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import {ErrorTolerantWalker} from './utils/ErrorTolerantWalker';
import {ExtendedMetadata} from './utils/ExtendedMetadata';
import {SyntaxKind} from './utils/SyntaxKind';

const FAILURE_STRING: string = 'An html element is missing the lang attribute';
const FAILURE_MISSING_LANG: string = 'An html element is missing the lang attribute';
const FAILURE_WRONG_LANG_CODE: string = 'Lang attribute does not have a valid value.';

/**
* Implementation of the react-a11y-lang rule.
Expand All @@ -15,7 +16,7 @@ export class Rule extends Lint.Rules.AbstractRule {
public static metadata: ExtendedMetadata = {
ruleName: 'react-a11y-lang',
type: 'functionality',
description: '... add a meaningful one line description',
description: 'html element should have a valid lang attribute',
options: null,
issueClass: 'Ignored',
issueType: 'Warning',
Expand All @@ -30,6 +31,19 @@ export class Rule extends Lint.Rules.AbstractRule {
}

class ReactA11yLangRuleWalker extends ErrorTolerantWalker {
private static languagesISO = [
'ab', 'aa', 'af', 'sq', 'am', 'ar', 'an', 'hy', 'as', 'ay', 'az', 'ba', 'eu', 'bn',
'dz', 'bh', 'bi', 'br', 'bg', 'my', 'be', 'km', 'ca', 'zh', 'zh-Hans', 'zh-Hant',
'co', 'hr', 'cs', 'da', 'nl', 'en', 'eo', 'et', 'fo', 'fa', 'fj', 'fi', 'fr', 'fy',
'gl', 'gd', 'gv', 'ka', 'de', 'el', 'kl', 'gn', 'gu', 'ht', 'ha', 'he', 'iw', 'hi',
'hu', 'is', 'io', 'id', 'in', 'ia', 'ie', 'iu', 'ik', 'ga', 'it', 'ja', 'jv', 'kn',
'ks', 'kk', 'rw', 'ky', 'rn', 'ko', 'ku', 'lo', 'la', 'lv', 'li', 'ln', 'lt', 'mk',
'mg', 'ms', 'ml', 'mt', 'mi', 'mr', 'mo', 'mn', 'na', 'ne', 'no', 'oc', 'or', 'om',
'ps', 'pl', 'pt', 'pa', 'qu', 'rm', 'ro', 'ru', 'sm', 'sg', 'sa', 'sr', 'sh', 'st',
'tn', 'sn', 'ii', 'sd', 'si', 'ss', 'sk', 'sl', 'so', 'es', 'su', 'sw', 'sv', 'tl',
'tg', 'ta', 'tt', 'te', 'th', 'bo', 'ti', 'to', 'ts', 'tr', 'tk', 'tw', 'ug', 'uk',
'ur', 'uz', 'vi', 'vo', 'wa', 'cy', 'wo', 'xh', 'yi', 'ji', 'yo', 'zu'
];

protected visitJsxSelfClosingElement(node: ts.JsxSelfClosingElement): void {
this.validateOpeningElement(node, node);
Expand All @@ -44,16 +58,26 @@ class ReactA11yLangRuleWalker extends ErrorTolerantWalker {
private validateOpeningElement(parent: ts.Node, openingElement: ts.JsxOpeningElement): void {
if (openingElement.tagName.getText() === 'html') {
const attributes: ts.NodeArray<ts.JsxAttribute | ts.JsxSpreadAttribute> = openingElement.attributes;
let found: boolean = false;
let langFound: boolean = false;
let validLangCode: boolean = false;

attributes.forEach((attribute: ts.JsxAttribute | ts.JsxSpreadAttribute): void => {
if (attribute.kind === SyntaxKind.current().JsxAttribute) {
if ((<ts.JsxAttribute>attribute).name.getText() === 'lang') {
found = true;
langFound = true;
let langText: string = (<ts.JsxAttribute>attribute).initializer.getText().trim();
langText = langText.slice(1, -1); // 'en' -> en
if ((ReactA11yLangRuleWalker.languagesISO.indexOf(langText)) > -1) {
validLangCode = true;
}
}
}
});
if (!found) {
this.addFailure(this.createFailure(parent.getStart(), parent.getWidth(), FAILURE_STRING));

if (!langFound) {
this.addFailure(this.createFailure(parent.getStart(), parent.getWidth(), FAILURE_MISSING_LANG));
} else if (!validLangCode) {
this.addFailure(this.createFailure(parent.getStart(), parent.getWidth(), FAILURE_WRONG_LANG_CODE));
}
}
}
Expand Down
29 changes: 27 additions & 2 deletions tests/ReactA11yLangRuleTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ describe('reactA11yLangRule', () : void => {
const script : string = `
import React = require('react');
const x = <html lang='en'></html>;
const y = <html lang='en' />;
const x = <html lang='en' title="asdf"></html>;
const y = <html lang='no' />;
`;

TestHelper.assertViolations(ruleName, script, [ ]);
Expand Down Expand Up @@ -54,4 +54,29 @@ describe('reactA11yLangRule', () : void => {
}
]);
});

it('should fail on invalid language code', () : void => {
const script : string = `
import React = require('react');
const x = <html lang='foo'></html>;
const y = <html lang='bar' />;
`;

TestHelper.assertViolations(ruleName, script, [
{
"failure": "Lang attribute does not have a valid value.",
"name": "file.tsx",
"ruleName": "react-a11y-lang",
"startPosition": { "character": 23, "line": 4 }
},
{
"failure": "Lang attribute does not have a valid value.",
"name": "file.tsx",
"ruleName": "react-a11y-lang",
"startPosition": { "character": 23, "line": 5 }
}
]);
});

});

0 comments on commit 41b9565

Please sign in to comment.