react-a11y
Version:
Warns about potential accessibility issues with your React elements.
200 lines (152 loc) • 7.13 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
var _extends2 = require('babel-runtime/helpers/extends');
var _extends3 = _interopRequireDefault(_extends2);
var _typeof2 = require('babel-runtime/helpers/typeof');
var _typeof3 = _interopRequireDefault(_typeof2);
var _objectWithoutProperties2 = require('babel-runtime/helpers/objectWithoutProperties');
var _objectWithoutProperties3 = _interopRequireDefault(_objectWithoutProperties2);
var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck');
var _classCallCheck3 = _interopRequireDefault(_classCallCheck2);
var _createClass2 = require('babel-runtime/helpers/createClass');
var _createClass3 = _interopRequireDefault(_createClass2);
var _after = require('./after');
var _after2 = _interopRequireDefault(_after);
var _options2 = require('./options');
var _options3 = _interopRequireDefault(_options2);
var _browser = require('./util/browser');
var _browser2 = _interopRequireDefault(_browser);
var _test = require('./test');
var _test2 = _interopRequireDefault(_test);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var A11y = function () {
/**
* @arg {object} React - The React instance you want to patch
* @arg {object} ReactDOM - The ReactDOM instance you'll be using
* @arg {object} options - the options
* @returns {A11y} The react-a11y instance
*/
function A11y() {
(0, _classCallCheck3.default)(this, A11y);
var _validate = _options3.default.apply(undefined, arguments),
React = _validate.React,
ReactDOM = _validate.ReactDOM,
options = (0, _objectWithoutProperties3.default)(_validate, ['React', 'ReactDOM']);
this.options = options;
this.React = React;
this.ReactDOM = ReactDOM;
this.suite = new _test2.default(React, ReactDOM, this.options);
this.patchReact();
}
/**
* Patch React, replacing its createElement by our implementation
* so we can run the tests
* @returns {undefined}
*/
(0, _createClass3.default)(A11y, [{
key: 'patchReact',
value: function patchReact() {
// save old createElement
this._createElement = this.React.createElement;
var _this = this;
this.React.createElement = function (klass) {
var _props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
// fix for props = null
var props = _props || {};
// create a refs object to hold the ref.
// this needs to be an object so that it can be passed
// by reference, and hold changing state.
var refs = typeof props.ref === 'string' || (0, _typeof3.default)(props.ref) === 'object' ? props.ref : {};
var ref = typeof props.ref === 'string' || (0, _typeof3.default)(props.ref) === 'object' ? props.ref : function (node) {
refs.node = node;
// maintain behaviour when ref prop was already set
if (typeof props.ref === 'function') {
props.ref(node);
}
};
var newProps = typeof klass === 'string' ? (0, _extends3.default)({}, props, { ref: ref }) : props;
for (var _len = arguments.length, children = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) {
children[_key - 2] = arguments[_key];
}
var reactEl = _this._createElement.apply(_this, [klass, newProps].concat(children));
// only test html elements
if (typeof klass === 'string') {
var handler = _this.failureHandler(reactEl, refs);
var childrenForTest = children.length === 0 ? props.children || [] : children;
_this.suite.test(klass, props, childrenForTest, handler);
}
return reactEl;
};
}
/**
* Restore React and all components as if we were never here
* @returns {undefined}
*/
}, {
key: 'restoreAll',
value: function restoreAll() {
this.React.createElement = this._createElement;
_after2.default.restorePatchedMethods();
}
/**
* Creates a failure handler based on the element that was created
* @arg {object} reactEl - The react element this failure is for
* @arg {object} ref - The object that holds the DOM node (passed by ref)
* @returns {function} A handler that knows everything it needs to know
*/
}, {
key: 'failureHandler',
value: function failureHandler(reactEl, ref) {
var _options = this.options,
reporter = _options.reporter,
filterFn = _options.filterFn;
/**
* @arg {string} errInfo - All the error info (see docs what this means)
* @returns {undefined}
*/
return function (errInfo) {
// get the owning component (the one that has
// the element in its render fn)
var owner = reactEl._owner;
// if there is an owner, use its name
// if not, use the tagname of the violating elemnent
var displayName = '';
if (owner) {
displayName = owner.type ? owner.type.name : owner.getName();
} else {
displayName = errInfo.tagName + '#' + errInfo.props.id;
}
// stop if we're not allowed to proceed
if (!filterFn(displayName, errInfo.props.id, errInfo.msg)) {
return;
}
// gather all info for the reporter
var info = (0, _extends3.default)({}, errInfo, {
displayName: displayName
});
var DOMNode = false;
if (_browser2.default && !this.__sync) {
// Make a best-effort attempt to grab the DOMNode
var instance = owner && owner._instance;
if (owner && owner.stateNode) {
// Fiber
DOMNode = this.ReactDOM.findDOMNode(owner.stateNode);
} else if (typeof ref === 'string' && instance) {
DOMNode = this.ReactDOM.findDOMNode(instance.refs[ref]); // TODO: replace use of findDOMNode
} else if ('node' in ref) {
DOMNode = ref.node;
}
}
if (DOMNode) {
reporter((0, _extends3.default)({}, info, { DOMNode: DOMNode }));
} else {
reporter(info);
}
}.bind(this);
}
}]);
return A11y;
}();
exports.default = A11y;