UNPKG

react-a11y

Version:

Warns about potential accessibility issues with your React elements.

200 lines (152 loc) 7.13 kB
'use strict'; 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;