diff --git a/karma.conf.js b/karma.conf.js
index bb1f22e..f3d7f60 100644
--- a/karma.conf.js
+++ b/karma.conf.js
@@ -25,7 +25,7 @@ module.exports = function (config) {
devtool: 'inline-source-map',
module: {
loaders: [
- { test: /\.js$/, loader: 'babel-loader' }
+ { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader?optional=runtime' }
]
},
plugins: [
diff --git a/lib/__tests__/index-test.js b/lib/__tests__/index-test.js
index 000f44e..ad760a6 100644
--- a/lib/__tests__/index-test.js
+++ b/lib/__tests__/index-test.js
@@ -125,7 +125,7 @@ describe('tags', () => {
});
it('dissallows the word "image" in the alt attribute', () => {
- expectWarning(assertions.tags.img.REDUDANT_ALT.msg, () => {
+ expectWarning(assertions.tags.img.REDUNDANT_ALT.msg, () => {
;
});
});
@@ -524,4 +524,24 @@ describe('device is set to mobile', () => {
});
});
});
-});
\ No newline at end of file
+});
+
+describe('exclusions', () => {
+ var createElement = React.createElement;
+
+ before(() => {
+ a11y(React, { exclude: ['REDUNDANT_ALT'] });
+ });
+
+ after(() => {
+ React.createElement = createElement;
+ });
+
+ describe('when REDUNDANT_ALT is excluded', () => {
+ it('does not warn when the word "image" in the alt attribute', () => {
+ doNotExpectWarning(assertions.tags.img.REDUNDANT_ALT.msg, () => {
+ ;
+ });
+ });
+ });
+});
diff --git a/lib/assertions.js b/lib/assertions.js
index 764c45d..a6534a5 100644
--- a/lib/assertions.js
+++ b/lib/assertions.js
@@ -21,8 +21,6 @@ var INTERACTIVE = {
}
};
-const DEVICE = { 'DESKTOP': 'desktop', 'MOBILE': 'mobile' };
-
var hasAlt = (props) => {
return typeof props.alt === 'string';
};
@@ -117,7 +115,6 @@ exports.props = {
},
NO_TABINDEX: {
- device: DEVICE.DESKTOP,
msg: 'You have a click handler on a non-interactive element but no `tabIndex` DOM property. The element will not be navigable or interactive by keyboard users. http://www.w3.org/TR/wai-aria-practices/#focus_tabindex',
test (tagName, props, children) {
return !(
@@ -128,7 +125,6 @@ exports.props = {
},
BUTTON_ROLE_SPACE: {
- device: DEVICE.DESKTOP,
msg: 'You have `role="button"` but did not define an `onKeyDown` handler. Add it, and have the "Space" key do the same thing as an `onClick` handler.',
test (tagName, props, children) {
return !(props.role === 'button' && !props.onKeyDown);
@@ -136,7 +132,6 @@ exports.props = {
},
BUTTON_ROLE_ENTER: {
- device: DEVICE.DESKTOP,
msg: 'You have `role="button"` but did not define an `onKeyDown` handler. Add it, and have the "Enter" key do the same thing as an `onClick` handler.',
test (tagName, props, children) {
return !(props.role === 'button' && !props.onKeyDown);
@@ -170,7 +165,7 @@ exports.tags = {
}
},
- REDUDANT_ALT: {
+ REDUNDANT_ALT: {
// TODO: have some way to set localization strings to match against
msg: 'Screen-readers already announce `img` tags as an image, you don\'t need to use the word "image" in the description',
test (tagName, props, children) {
diff --git a/lib/index.js b/lib/index.js
index de5df9f..95539d7 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -1,20 +1,29 @@
var assertions = require('./assertions');
var after = require('./after');
-var deviceMatches = (test, deviceFilter) => {
- if (!test.device)
- return true;
+const mobileExclusions = [
+ 'NO_TABINDEX',
+ 'BUTTON_ROLE_SPACE',
+ 'BUTTON_ROLE_ENTER'
+];
+
+var shouldRunTest = (testName, options) => {
+ var exclude = options.exclude || [];
+
+ if (options.device == 'mobile') {
+ exclude = new Set(exclude.concat(mobileExclusions));
+ exclude = [...exclude];
+ }
- return (deviceFilter.indexOf(test.device) != -1);
+ return (exclude.indexOf(testName) == -1);
};
-var runTagTests = (tagName, props, children, deviceFilter, onFailure) => {
+var runTagTests = (tagName, props, children, options, onFailure) => {
var key;
var tagTests = assertions.tags[tagName] || [];
for (key in tagTests) {
- let shouldRunTest = deviceMatches(tagTests[key], deviceFilter);
- let testFailed = shouldRunTest &&
+ let testFailed = shouldRunTest(key, options) &&
!tagTests[key].test(tagName, props, children);
if (tagTests[key] && testFailed)
@@ -22,7 +31,7 @@ var runTagTests = (tagName, props, children, deviceFilter, onFailure) => {
}
};
-var runPropTests = (tagName, props, children, deviceFilter, onFailure) => {
+var runPropTests = (tagName, props, children, options, onFailure) => {
var key;
var propTests;
@@ -32,8 +41,7 @@ var runPropTests = (tagName, props, children, deviceFilter, onFailure) => {
propTests = assertions.props[propName] || [];
for (key in propTests) {
- let shouldRunTest = deviceMatches(propTests[key], deviceFilter);
- let testTailed = shouldRunTest &&
+ let testTailed = shouldRunTest(key, options) &&
!propTests[key].test(tagName, props, children);
if (propTests[key] && testTailed)
@@ -42,12 +50,12 @@ var runPropTests = (tagName, props, children, deviceFilter, onFailure) => {
}
};
-var runLabelTests = (tagName, props, children, deviceFilter, onFailure) => {
+var runLabelTests = (tagName, props, children, options, onFailure) => {
var key;
var renderTests = assertions.render;
for (key in renderTests) {
- if (renderTests[key]) {
+ if (shouldRunTest(key, options) && renderTests[key]) {
let failureCB = onFailure.bind(
undefined, tagName, props, renderTests[key].msg);
@@ -56,10 +64,10 @@ var runLabelTests = (tagName, props, children, deviceFilter, onFailure) => {
}
};
-var runTests = (tagName, props, children, deviceFilter, onFailure) => {
+var runTests = (tagName, props, children, options, onFailure) => {
var tests = [runTagTests, runPropTests, runLabelTests];
tests.map((test) => {
- test(tagName, props, children, deviceFilter, onFailure);
+ test(tagName, props, children, options, onFailure);
});
};
@@ -168,17 +176,17 @@ var reactA11y = (React, options) => {
assertions.setReact(React);
_createElement = React.createElement;
- var deviceFilter = options && options.device || ['desktop'];
React.createElement = (type, _props, ...children) => {
var props = _props || {};
+ options = options || {};
props.id = createId(props);
var reactEl = _createElement.apply(this, [type, props].concat(children));
var failureCB = handleFailure.bind(undefined, options, reactEl);
if (typeof type === 'string')
- runTests(type, props, children, deviceFilter, failureCB);
+ runTests(type, props, children, options, failureCB);
return reactEl;
};
diff --git a/package.json b/package.json
index 33b4074..e2d5b70 100644
--- a/package.json
+++ b/package.json
@@ -12,7 +12,7 @@
"scripts": {
"test": "jsxhint . && karma start --single-run",
"watch-tests": "npm test -- --watch",
- "prepublish": "babel -d dist lib",
+ "prepublish": "babel --optional runtime lib --out-dir dist",
"release": "release"
},
"authors": [
@@ -25,6 +25,7 @@
"babel": "^5.2.17",
"babel-core": "^5.2.17",
"babel-loader": "^5.0.0",
+ "babel-runtime": "^5.1.11",
"jsx-loader": "^0.12.2",
"jsxhint": "^0.8.1",
"karma": "^0.12.28",
diff --git a/webpack.config.js b/webpack.config.js
index 74a5e0a..c7934d8 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -35,7 +35,7 @@ module.exports = {
module: {
loaders: [
- { test: /\.js$/, loader: 'jsx-loader?harmony' }
+ { test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader?optional=runtime' }
]
}