Izmjenjena struktura, dodan backand

This commit is contained in:
GotPPay
2017-10-16 11:19:46 +02:00
parent 1ec88afacb
commit 048e32c4aa
37153 changed files with 2975854 additions and 1 deletions

141
web/node_modules/eslint-plugin-jsx-a11y/lib/index.js generated vendored Normal file
View File

@@ -0,0 +1,141 @@
'use strict';
/* eslint-disable global-require */
module.exports = {
rules: {
'accessible-emoji': require('./rules/accessible-emoji'),
'alt-text': require('./rules/alt-text'),
'anchor-has-content': require('./rules/anchor-has-content'),
'aria-activedescendant-has-tabindex': require('./rules/aria-activedescendant-has-tabindex'),
'aria-props': require('./rules/aria-props'),
'aria-proptypes': require('./rules/aria-proptypes'),
'aria-role': require('./rules/aria-role'),
'aria-unsupported-elements': require('./rules/aria-unsupported-elements'),
'click-events-have-key-events': require('./rules/click-events-have-key-events'),
'heading-has-content': require('./rules/heading-has-content'),
'href-no-hash': require('./rules/href-no-hash'),
'html-has-lang': require('./rules/html-has-lang'),
'iframe-has-title': require('./rules/iframe-has-title'),
'img-redundant-alt': require('./rules/img-redundant-alt'),
'interactive-supports-focus': require('./rules/interactive-supports-focus'),
'label-has-for': require('./rules/label-has-for'),
lang: require('./rules/lang'),
'media-has-caption': require('./rules/media-has-caption'),
'mouse-events-have-key-events': require('./rules/mouse-events-have-key-events'),
'no-access-key': require('./rules/no-access-key'),
'no-autofocus': require('./rules/no-autofocus'),
'no-distracting-elements': require('./rules/no-distracting-elements'),
'no-interactive-element-to-noninteractive-role': require('./rules/no-interactive-element-to-noninteractive-role'),
'no-noninteractive-element-interactions': require('./rules/no-noninteractive-element-interactions'),
'no-noninteractive-element-to-interactive-role': require('./rules/no-noninteractive-element-to-interactive-role'),
'no-noninteractive-tabindex': require('./rules/no-noninteractive-tabindex'),
'no-onchange': require('./rules/no-onchange'),
'no-redundant-roles': require('./rules/no-redundant-roles'),
'no-static-element-interactions': require('./rules/no-static-element-interactions'),
'role-has-required-aria-props': require('./rules/role-has-required-aria-props'),
'role-supports-aria-props': require('./rules/role-supports-aria-props'),
scope: require('./rules/scope'),
'tabindex-no-positive': require('./rules/tabindex-no-positive')
},
configs: {
recommended: {
parserOptions: {
ecmaFeatures: {
jsx: true
}
},
rules: {
'jsx-a11y/accessible-emoji': 'error',
'jsx-a11y/alt-text': 'error',
'jsx-a11y/anchor-has-content': 'error',
'jsx-a11y/aria-activedescendant-has-tabindex': 'error',
'jsx-a11y/aria-props': 'error',
'jsx-a11y/aria-proptypes': 'error',
'jsx-a11y/aria-role': 'error',
'jsx-a11y/aria-unsupported-elements': 'error',
'jsx-a11y/click-events-have-key-events': 'error',
'jsx-a11y/heading-has-content': 'error',
'jsx-a11y/href-no-hash': 'error',
'jsx-a11y/html-has-lang': 'error',
'jsx-a11y/iframe-has-title': 'error',
'jsx-a11y/img-redundant-alt': 'error',
'jsx-a11y/interactive-supports-focus': 'error',
'jsx-a11y/label-has-for': 'error',
'jsx-a11y/media-has-caption': 'error',
'jsx-a11y/mouse-events-have-key-events': 'error',
'jsx-a11y/no-access-key': 'error',
'jsx-a11y/no-autofocus': 'error',
'jsx-a11y/no-distracting-elements': 'error',
'jsx-a11y/no-interactive-element-to-noninteractive-role': ['error', {
tr: ['none', 'presentation']
}],
'jsx-a11y/no-noninteractive-element-interactions': ['error', {
handlers: ['onClick', 'onMouseDown', 'onMouseUp', 'onKeyPress', 'onKeyDown', 'onKeyUp']
}],
'jsx-a11y/no-noninteractive-element-to-interactive-role': ['error', {
ul: ['listbox', 'menu', 'menubar', 'radiogroup', 'tablist', 'tree', 'treegrid'],
ol: ['listbox', 'menu', 'menubar', 'radiogroup', 'tablist', 'tree', 'treegrid'],
li: ['menuitem', 'option', 'row', 'tab', 'treeitem'],
table: ['grid'],
td: ['gridcell']
}],
'jsx-a11y/no-noninteractive-tabindex': ['error', {
tags: [],
roles: ['tabpanel']
}],
'jsx-a11y/no-onchange': 'error',
'jsx-a11y/no-redundant-roles': 'error',
'jsx-a11y/no-static-element-interactions': ['error', {
handlers: ['onClick', 'onMouseDown', 'onMouseUp', 'onKeyPress', 'onKeyDown', 'onKeyUp']
}],
'jsx-a11y/role-has-required-aria-props': 'error',
'jsx-a11y/role-supports-aria-props': 'error',
'jsx-a11y/scope': 'error',
'jsx-a11y/tabindex-no-positive': 'error'
}
},
strict: {
parserOptions: {
ecmaFeatures: {
jsx: true
}
},
rules: {
'jsx-a11y/accessible-emoji': 'error',
'jsx-a11y/alt-text': 'error',
'jsx-a11y/anchor-has-content': 'error',
'jsx-a11y/aria-activedescendant-has-tabindex': 'error',
'jsx-a11y/aria-props': 'error',
'jsx-a11y/aria-proptypes': 'error',
'jsx-a11y/aria-role': 'error',
'jsx-a11y/aria-unsupported-elements': 'error',
'jsx-a11y/click-events-have-key-events': 'error',
'jsx-a11y/heading-has-content': 'error',
'jsx-a11y/href-no-hash': 'error',
'jsx-a11y/html-has-lang': 'error',
'jsx-a11y/iframe-has-title': 'error',
'jsx-a11y/img-redundant-alt': 'error',
'jsx-a11y/interactive-supports-focus': 'error',
'jsx-a11y/label-has-for': 'error',
'jsx-a11y/media-has-caption': 'error',
'jsx-a11y/mouse-events-have-key-events': 'error',
'jsx-a11y/no-access-key': 'error',
'jsx-a11y/no-autofocus': 'error',
'jsx-a11y/no-distracting-elements': 'error',
'jsx-a11y/no-interactive-element-to-noninteractive-role': 'error',
'jsx-a11y/no-noninteractive-element-interactions': 'error',
'jsx-a11y/no-noninteractive-element-to-interactive-role': 'error',
'jsx-a11y/no-noninteractive-tabindex': 'error',
'jsx-a11y/no-onchange': 'error',
'jsx-a11y/no-redundant-roles': 'error',
'jsx-a11y/no-static-element-interactions': 'error',
'jsx-a11y/role-has-required-aria-props': 'error',
'jsx-a11y/role-supports-aria-props': 'error',
'jsx-a11y/scope': 'error',
'jsx-a11y/tabindex-no-positive': 'error'
}
}
}
};

View File

@@ -0,0 +1,54 @@
'use strict';
var _emojiRegex = require('emoji-regex');
var _emojiRegex2 = _interopRequireDefault(_emojiRegex);
var _jsxAstUtils = require('jsx-ast-utils');
var _schemas = require('../util/schemas');
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var errorMessage = 'Emojis should be wrapped in <span>, have role="img", and have an accessible description with aria-label or aria-labelledby.'; /**
* @fileoverview Enforce emojis are wrapped in <span> and provide screenreader access.
* @author Ethan Cohen
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var schema = (0, _schemas.generateObjSchema)();
module.exports = {
meta: {
docs: {},
schema: [schema]
},
create: function create(context) {
return {
JSXOpeningElement: function JSXOpeningElement(node) {
var literalChildValue = node.parent.children.find(function (child) {
return child.type === 'Literal';
});
if (literalChildValue && (0, _emojiRegex2.default)().test(literalChildValue.value)) {
var rolePropValue = (0, _jsxAstUtils.getLiteralPropValue)((0, _jsxAstUtils.getProp)(node.attributes, 'role'));
var ariaLabelProp = (0, _jsxAstUtils.getProp)(node.attributes, 'aria-label');
var arialLabelledByProp = (0, _jsxAstUtils.getProp)(node.attributes, 'aria-labelledby');
var hasLabel = ariaLabelProp !== undefined || arialLabelledByProp !== undefined;
var isSpan = (0, _jsxAstUtils.elementType)(node) === 'span';
if (hasLabel === false || rolePropValue !== 'img' || isSpan === false) {
context.report({
node: node,
message: errorMessage
});
}
}
}
};
}
};

View File

@@ -0,0 +1,209 @@
'use strict';
var _jsxAstUtils = require('jsx-ast-utils');
var _schemas = require('../util/schemas');
var _hasAccessibleChild = require('../util/hasAccessibleChild');
var _hasAccessibleChild2 = _interopRequireDefault(_hasAccessibleChild);
var _isPresentationRole = require('../util/isPresentationRole');
var _isPresentationRole2 = _interopRequireDefault(_isPresentationRole);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } /**
* @fileoverview Enforce all elements that require alternative text have it.
* @author Ethan Cohen
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var DEFAULT_ELEMENTS = ['img', 'object', 'area', 'input[type="image"]'];
var schema = (0, _schemas.generateObjSchema)({
elements: _schemas.arraySchema,
img: _schemas.arraySchema,
object: _schemas.arraySchema,
area: _schemas.arraySchema,
'input[type="image"]': _schemas.arraySchema
});
var ruleByElement = {
img: function img(context, node) {
var nodeType = (0, _jsxAstUtils.elementType)(node);
var altProp = (0, _jsxAstUtils.getProp)(node.attributes, 'alt');
// Missing alt prop error.
if (altProp === undefined) {
if ((0, _isPresentationRole2.default)(nodeType, node.attributes)) {
context.report({
node: node,
message: 'Prefer alt="" over a presentational role. First rule of aria is to not use aria if it can be achieved via native HTML.'
});
return;
}
context.report({
node: node,
message: nodeType + ' elements must have an alt prop, either with meaningful text, or an empty string for decorative images.'
});
return;
}
// Check if alt prop is undefined.
var altValue = (0, _jsxAstUtils.getPropValue)(altProp);
var isNullValued = altProp.value === null; // <img alt />
if (altValue && !isNullValued || altValue === '') {
return;
}
// Undefined alt prop error.
context.report({
node: node,
message: 'Invalid alt value for ' + nodeType + '. Use alt="" for presentational images.'
});
},
object: function object(context, node) {
var ariaLabelProp = (0, _jsxAstUtils.getProp)(node.attributes, 'aria-label');
var arialLabelledByProp = (0, _jsxAstUtils.getProp)(node.attributes, 'aria-labelledby');
var hasLabel = ariaLabelProp !== undefined || arialLabelledByProp !== undefined;
var titleProp = (0, _jsxAstUtils.getLiteralPropValue)((0, _jsxAstUtils.getProp)(node.attributes, 'title'));
var hasTitleAttr = !!titleProp;
if (hasLabel || hasTitleAttr || (0, _hasAccessibleChild2.default)(node.parent)) {
return;
}
context.report({
node: node,
message: 'Embedded <object> elements must have alternative text by providing inner text, aria-label or aria-labelledby props.'
});
},
area: function area(context, node) {
var ariaLabelPropValue = (0, _jsxAstUtils.getPropValue)((0, _jsxAstUtils.getProp)(node.attributes, 'aria-label'));
var arialLabelledByPropValue = (0, _jsxAstUtils.getPropValue)((0, _jsxAstUtils.getProp)(node.attributes, 'aria-labelledby'));
var hasLabel = ariaLabelPropValue !== undefined || arialLabelledByPropValue !== undefined;
if (hasLabel) {
return;
}
var altProp = (0, _jsxAstUtils.getProp)(node.attributes, 'alt');
if (altProp === undefined) {
context.report({
node: node,
message: 'Each area of an image map must have a text alternative through the `alt`, `aria-label`, or `aria-labelledby` prop.'
});
return;
}
var altValue = (0, _jsxAstUtils.getPropValue)(altProp);
var isNullValued = altProp.value === null; // <area alt />
if (altValue && !isNullValued || altValue === '') {
return;
}
context.report({
node: node,
message: 'Each area of an image map must have a text alternative through the `alt`, `aria-label`, or `aria-labelledby` prop.'
});
},
'input[type="image"]': function inputImage(context, node) {
// Only test input[type="image"]
var nodeType = (0, _jsxAstUtils.elementType)(node);
if (nodeType === 'input') {
var typePropValue = (0, _jsxAstUtils.getPropValue)((0, _jsxAstUtils.getProp)(node.attributes, 'type'));
if (typePropValue !== 'image') {
return;
}
}
var ariaLabelPropValue = (0, _jsxAstUtils.getPropValue)((0, _jsxAstUtils.getProp)(node.attributes, 'aria-label'));
var arialLabelledByPropValue = (0, _jsxAstUtils.getPropValue)((0, _jsxAstUtils.getProp)(node.attributes, 'aria-labelledby'));
var hasLabel = ariaLabelPropValue !== undefined || arialLabelledByPropValue !== undefined;
if (hasLabel) {
return;
}
var altProp = (0, _jsxAstUtils.getProp)(node.attributes, 'alt');
if (altProp === undefined) {
context.report({
node: node,
message: '<input> elements with type="image" must have a text alternative through the `alt`, `aria-label`, or `aria-labelledby` prop.'
});
return;
}
var altValue = (0, _jsxAstUtils.getPropValue)(altProp);
var isNullValued = altProp.value === null; // <area alt />
if (altValue && !isNullValued || altValue === '') {
return;
}
context.report({
node: node,
message: '<input> elements with type="image" must have a text alternative through the `alt`, `aria-label`, or `aria-labelledby` prop.'
});
}
};
module.exports = {
meta: {
docs: {},
schema: [schema]
},
create: function create(context) {
var _ref;
var options = context.options[0] || {};
// Elements to validate for alt text.
var elementOptions = options.elements || DEFAULT_ELEMENTS;
// Get custom components for just the elements that will be tested.
var customComponents = elementOptions.map(function (element) {
return options[element];
}).reduce(function (components, customComponentsForElement) {
return components.concat(customComponentsForElement || []);
}, []);
var typesToValidate = new Set((_ref = []).concat.apply(_ref, [customComponents].concat(_toConsumableArray(elementOptions))).map(function (type) {
if (type === 'input[type="image"]') {
return 'input';
}
return type;
}));
return {
JSXOpeningElement: function JSXOpeningElement(node) {
var nodeType = (0, _jsxAstUtils.elementType)(node);
if (!typesToValidate.has(nodeType)) {
return;
}
var DOMElement = nodeType;
if (DOMElement === 'input') {
DOMElement = 'input[type="image"]';
}
// Map nodeType to the DOM element if we are running this on a custom component.
if (elementOptions.indexOf(DOMElement) === -1) {
DOMElement = elementOptions.find(function (element) {
var customComponentsForElement = options[element] || [];
return customComponentsForElement.indexOf(nodeType) > -1;
});
}
ruleByElement[DOMElement](context, node);
}
};
}
};

View File

@@ -0,0 +1,52 @@
'use strict';
var _jsxAstUtils = require('jsx-ast-utils');
var _schemas = require('../util/schemas');
var _hasAccessibleChild = require('../util/hasAccessibleChild');
var _hasAccessibleChild2 = _interopRequireDefault(_hasAccessibleChild);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var errorMessage = 'Anchors must have content and the content must be accessible by a screen reader.'; /**
* @fileoverview Enforce anchor elements to contain accessible content.
* @author Lisa Ring & Niklas Holmberg
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var schema = (0, _schemas.generateObjSchema)({ components: _schemas.arraySchema });
module.exports = {
meta: {
docs: {},
schema: [schema]
},
create: function create(context) {
return {
JSXOpeningElement: function JSXOpeningElement(node) {
var options = context.options[0] || {};
var componentOptions = options.components || [];
var typeCheck = ['a'].concat(componentOptions);
var nodeType = (0, _jsxAstUtils.elementType)(node);
// Only check anchor elements and custom types.
if (typeCheck.indexOf(nodeType) === -1) {
return;
} else if ((0, _hasAccessibleChild2.default)(node.parent)) {
return;
}
context.report({
node: node,
message: errorMessage
});
}
};
}
};

View File

@@ -0,0 +1,77 @@
'use strict';
var _ariaQuery = require('aria-query');
var _jsxAstUtils = require('jsx-ast-utils');
var _schemas = require('../util/schemas');
var _getTabIndex = require('../util/getTabIndex');
var _getTabIndex2 = _interopRequireDefault(_getTabIndex);
var _isInteractiveElement = require('../util/isInteractiveElement');
var _isInteractiveElement2 = _interopRequireDefault(_isInteractiveElement);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } /**
* @fileoverview Enforce elements with aria-activedescendant are tabbable.
* @author Jesse Beach <@jessebeach>
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var errorMessage = 'An element that manages focus with `aria-activedescendant` must be tabbable';
var schema = (0, _schemas.generateObjSchema)();
var domElements = [].concat(_toConsumableArray(_ariaQuery.dom.keys()));
module.exports = {
meta: {
docs: {},
schema: [schema]
},
create: function create(context) {
return {
JSXOpeningElement: function JSXOpeningElement(node) {
var attributes = node.attributes;
if ((0, _jsxAstUtils.getProp)(attributes, 'aria-activedescendant') === undefined) {
return;
}
var type = (0, _jsxAstUtils.elementType)(node);
// Do not test higher level JSX components, as we do not know what
// low-level DOM element this maps to.
if (domElements.indexOf(type) === -1) {
return;
}
var tabIndex = (0, _getTabIndex2.default)((0, _jsxAstUtils.getProp)(attributes, 'tabIndex'));
// If this is an interactive element, tabIndex must be either left
// unspecified allowing the inherent tabIndex to obtain or it must be
// zero (allowing for positive, even though that is not ideal). It cannot
// be given a negative value.
if ((0, _isInteractiveElement2.default)(type, attributes) && (tabIndex === undefined || tabIndex >= 0)) {
return;
}
if (tabIndex >= 0) {
return;
}
context.report({
node: node,
message: errorMessage
});
}
};
}
};

View File

@@ -0,0 +1,67 @@
'use strict';
var _ariaQuery = require('aria-query');
var _jsxAstUtils = require('jsx-ast-utils');
var _schemas = require('../util/schemas');
var _getSuggestion = require('../util/getSuggestion');
var _getSuggestion2 = _interopRequireDefault(_getSuggestion);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } /**
* @fileoverview Enforce all aria-* properties are valid.
* @author Ethan Cohen
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var ariaAttributes = [].concat(_toConsumableArray(_ariaQuery.aria.keys()));
var errorMessage = function errorMessage(name) {
var suggestions = (0, _getSuggestion2.default)(name, ariaAttributes);
var message = name + ': This attribute is an invalid ARIA attribute.';
if (suggestions.length > 0) {
return message + ' Did you mean to use ' + suggestions + '?';
}
return message;
};
var schema = (0, _schemas.generateObjSchema)();
module.exports = {
meta: {
docs: {},
schema: [schema]
},
create: function create(context) {
return {
JSXAttribute: function JSXAttribute(attribute) {
var name = (0, _jsxAstUtils.propName)(attribute);
var normalizedName = name ? name.toLowerCase() : '';
// `aria` needs to be prefix of property.
if (normalizedName.indexOf('aria-') !== 0) {
return;
}
var isValid = ariaAttributes.indexOf(normalizedName) > -1;
if (isValid === false) {
context.report({
node: attribute,
message: errorMessage(name)
});
}
}
};
}
};

View File

@@ -0,0 +1,102 @@
'use strict';
var _ariaQuery = require('aria-query');
var _jsxAstUtils = require('jsx-ast-utils');
var _schemas = require('../util/schemas');
var errorMessage = function errorMessage(name, type, permittedValues) {
switch (type) {
case 'tristate':
return 'The value for ' + name + ' must be a boolean or the string "mixed".';
case 'token':
return 'The value for ' + name + ' must be a single token from the following: ' + permittedValues + '.';
case 'tokenlist':
return 'The value for ' + name + ' must be a list of one or more tokens from the following: ' + permittedValues + '.';
case 'boolean':
case 'string':
case 'integer':
case 'number':
default:
return 'The value for ' + name + ' must be a ' + type + '.';
}
}; /**
* @fileoverview Enforce ARIA state and property values are valid.
* @author Ethan Cohen
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var validityCheck = function validityCheck(value, expectedType, permittedValues) {
switch (expectedType) {
case 'boolean':
return typeof value === 'boolean';
case 'string':
return typeof value === 'string';
case 'tristate':
return typeof value === 'boolean' || value === 'mixed';
case 'integer':
case 'number':
// Booleans resolve to 0/1 values so hard check that it's not first.
return typeof value !== 'boolean' && isNaN(Number(value)) === false;
case 'token':
return permittedValues.indexOf(typeof value === 'string' ? value.toLowerCase() : value) > -1;
case 'tokenlist':
return typeof value === 'string' && value.split(' ').every(function (token) {
return permittedValues.indexOf(token.toLowerCase()) > -1;
});
default:
return false;
}
};
var schema = (0, _schemas.generateObjSchema)();
module.exports = {
validityCheck: validityCheck,
meta: {
docs: {},
schema: [schema]
},
create: function create(context) {
return {
JSXAttribute: function JSXAttribute(attribute) {
var name = (0, _jsxAstUtils.propName)(attribute);
var normalizedName = name ? name.toLowerCase() : '';
// Not a valid aria-* state or property.
if (normalizedName.indexOf('aria-') !== 0 || _ariaQuery.aria.get(normalizedName) === undefined) {
return;
}
var value = (0, _jsxAstUtils.getLiteralPropValue)(attribute);
// We only want to check literal prop values, so just pass if it's null.
if (value === null) {
return;
}
// These are the attributes of the property/state to check against.
var attributes = _ariaQuery.aria.get(normalizedName);
var permittedType = attributes.type;
var allowUndefined = attributes.allowUndefined || false;
var permittedValues = attributes.values || [];
var isValid = validityCheck(value, permittedType, permittedValues) || allowUndefined && value === undefined;
if (isValid) {
return;
}
context.report({
node: attribute,
message: errorMessage(name, permittedType, permittedValues)
});
}
};
}
};

View File

@@ -0,0 +1,84 @@
'use strict';
var _ariaQuery = require('aria-query');
var _jsxAstUtils = require('jsx-ast-utils');
var _schemas = require('../util/schemas');
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } /**
* @fileoverview Enforce aria role attribute is valid.
* @author Ethan Cohen
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var errorMessage = 'Elements with ARIA roles must use a valid, non-abstract ARIA role.';
var schema = (0, _schemas.generateObjSchema)({
ignoreNonDOM: {
type: 'boolean',
default: false
}
});
module.exports = {
meta: {
docs: {},
schema: [schema]
},
create: function create(context) {
return {
JSXAttribute: function JSXAttribute(attribute) {
// Determine if ignoreNonDOM is set to true
// If true, then do not run rule.
var options = context.options[0] || {};
var ignoreNonDOM = !!options.ignoreNonDOM;
if (ignoreNonDOM) {
var type = (0, _jsxAstUtils.elementType)(attribute.parent);
if (!_ariaQuery.dom.get(type)) {
return;
}
}
// Get prop name
var name = (0, _jsxAstUtils.propName)(attribute);
var normalizedName = name ? name.toUpperCase() : '';
if (normalizedName !== 'ROLE') {
return;
}
var value = (0, _jsxAstUtils.getLiteralPropValue)(attribute);
// If value is undefined, then the role attribute will be dropped in the DOM.
// If value is null, then getLiteralAttributeValue is telling us that the
// value isn't in the form of a literal.
if (value === undefined || value === null) {
return;
}
var normalizedValues = String(value).toLowerCase().split(' ');
var validRoles = [].concat(_toConsumableArray(_ariaQuery.roles.keys())).filter(function (role) {
return _ariaQuery.roles.get(role).abstract === false;
});
var isValid = normalizedValues.every(function (val) {
return validRoles.indexOf(val) > -1;
});
if (isValid === true) {
return;
}
context.report({
node: attribute,
message: errorMessage
});
}
};
}
};

View File

@@ -0,0 +1,65 @@
'use strict';
var _ariaQuery = require('aria-query');
var _jsxAstUtils = require('jsx-ast-utils');
var _schemas = require('../util/schemas');
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } /**
* @fileoverview Enforce that elements that do not support ARIA roles,
* states and properties do not have those attributes.
* @author Ethan Cohen
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var errorMessage = function errorMessage(invalidProp) {
return 'This element does not support ARIA roles, states and properties. Try removing the prop \'' + invalidProp + '\'.';
};
var schema = (0, _schemas.generateObjSchema)();
module.exports = {
meta: {
docs: {},
schema: [schema]
},
create: function create(context) {
return {
JSXOpeningElement: function JSXOpeningElement(node) {
var nodeType = (0, _jsxAstUtils.elementType)(node);
var nodeAttrs = _ariaQuery.dom.get(nodeType) || {};
var _nodeAttrs$reserved = nodeAttrs.reserved,
isReservedNodeType = _nodeAttrs$reserved === undefined ? false : _nodeAttrs$reserved;
// If it's not reserved, then it can have aria-* roles, states, and properties
if (isReservedNodeType === false) {
return;
}
var invalidAttributes = [].concat(_toConsumableArray(_ariaQuery.aria.keys())).concat('role');
node.attributes.forEach(function (prop) {
if (prop.type === 'JSXSpreadAttribute') {
return;
}
var name = (0, _jsxAstUtils.propName)(prop);
var normalizedName = name ? name.toLowerCase() : '';
if (invalidAttributes.indexOf(normalizedName) > -1) {
context.report({
node: node,
message: errorMessage(name)
});
}
});
}
};
}
};

View File

@@ -0,0 +1,74 @@
'use strict';
var _ariaQuery = require('aria-query');
var _jsxAstUtils = require('jsx-ast-utils');
var _arrayIncludes = require('array-includes');
var _arrayIncludes2 = _interopRequireDefault(_arrayIncludes);
var _schemas = require('../util/schemas');
var _isHiddenFromScreenReader = require('../util/isHiddenFromScreenReader');
var _isHiddenFromScreenReader2 = _interopRequireDefault(_isHiddenFromScreenReader);
var _isInteractiveElement = require('../util/isInteractiveElement');
var _isInteractiveElement2 = _interopRequireDefault(_isInteractiveElement);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } /**
* @fileoverview Enforce a clickable non-interactive element has at least 1 keyboard event listener.
* @author Ethan Cohen
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var errorMessage = 'Visible, non-interactive elements with click handlers' + ' must have at least one keyboard listener.';
var schema = (0, _schemas.generateObjSchema)();
var domElements = [].concat(_toConsumableArray(_ariaQuery.dom.keys()));
module.exports = {
meta: {
docs: {},
schema: [schema]
},
create: function create(context) {
return {
JSXOpeningElement: function JSXOpeningElement(node) {
var props = node.attributes;
if ((0, _jsxAstUtils.getProp)(props, 'onclick') === undefined) {
return;
}
var type = (0, _jsxAstUtils.elementType)(node);
var requiredProps = ['onkeydown', 'onkeyup', 'onkeypress'];
if (!(0, _arrayIncludes2.default)(domElements, type)) {
// Do not test higher level JSX components, as we do not know what
// low-level DOM element this maps to.
return;
} else if ((0, _isHiddenFromScreenReader2.default)(type, props)) {
return;
} else if ((0, _isInteractiveElement2.default)(type, props)) {
return;
} else if ((0, _jsxAstUtils.hasAnyProp)(props, requiredProps)) {
return;
}
// Visible, non-interactive elements with click handlers require one keyboard event listener.
context.report({
node: node,
message: errorMessage
});
}
};
}
};

View File

@@ -0,0 +1,52 @@
'use strict';
var _jsxAstUtils = require('jsx-ast-utils');
var _schemas = require('../util/schemas');
var _hasAccessibleChild = require('../util/hasAccessibleChild');
var _hasAccessibleChild2 = _interopRequireDefault(_hasAccessibleChild);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var errorMessage = 'Headings must have content and the content must be accessible by a screen reader.'; /**
* @fileoverview Enforce heading (h1, h2, etc) elements contain accessible content.
* @author Ethan Cohen
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var headings = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
var schema = (0, _schemas.generateObjSchema)({ components: _schemas.arraySchema });
module.exports = {
meta: {
docs: {},
schema: [schema]
},
create: function create(context) {
return {
JSXOpeningElement: function JSXOpeningElement(node) {
var typeCheck = headings.concat(context.options[0]);
var nodeType = (0, _jsxAstUtils.elementType)(node);
// Only check 'h*' elements and custom types.
if (typeCheck.indexOf(nodeType) === -1) {
return;
} else if ((0, _hasAccessibleChild2.default)(node.parent)) {
return;
}
context.report({
node: node,
message: errorMessage
});
}
};
}
};

View File

@@ -0,0 +1,61 @@
'use strict';
var _jsxAstUtils = require('jsx-ast-utils');
var _schemas = require('../util/schemas');
/**
* @fileoverview Enforce links may not point to just #.
* @author Ethan Cohen
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var errorMessage = 'Links must not point to "#". ' + 'Use a more descriptive href or use a button instead.';
var schema = (0, _schemas.generateObjSchema)({
components: _schemas.arraySchema,
specialLink: _schemas.arraySchema
});
module.exports = {
meta: {
docs: {},
schema: [schema]
},
create: function create(context) {
return {
JSXOpeningElement: function JSXOpeningElement(node) {
var options = context.options[0] || {};
var componentOptions = options.components || [];
var typesToValidate = ['a'].concat(componentOptions);
var nodeType = (0, _jsxAstUtils.elementType)(node);
// Only check 'a' elements and custom types.
if (typesToValidate.indexOf(nodeType) === -1) {
return;
}
var propOptions = options.specialLink || [];
var propsToValidate = ['href'].concat(propOptions);
var values = propsToValidate.map(function (prop) {
return (0, _jsxAstUtils.getProp)(node.attributes, prop);
}).map(function (prop) {
return (0, _jsxAstUtils.getPropValue)(prop);
});
values.forEach(function (value) {
if (value === '#') {
context.report({
node: node,
message: errorMessage
});
}
});
}
};
}
};

View File

@@ -0,0 +1,48 @@
'use strict';
var _jsxAstUtils = require('jsx-ast-utils');
var _schemas = require('../util/schemas');
/**
* @fileoverview Enforce html element has lang prop.
* @author Ethan Cohen
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var errorMessage = '<html> elements must have the lang prop.';
var schema = (0, _schemas.generateObjSchema)();
module.exports = {
meta: {
docs: {},
schema: [schema]
},
create: function create(context) {
return {
JSXOpeningElement: function JSXOpeningElement(node) {
var type = (0, _jsxAstUtils.elementType)(node);
if (type && type !== 'html') {
return;
}
var lang = (0, _jsxAstUtils.getPropValue)((0, _jsxAstUtils.getProp)(node.attributes, 'lang'));
if (lang) {
return;
}
context.report({
node: node,
message: errorMessage
});
}
};
}
};

View File

@@ -0,0 +1,48 @@
'use strict';
var _jsxAstUtils = require('jsx-ast-utils');
var _schemas = require('../util/schemas');
/**
* @fileoverview Enforce iframe elements have a title attribute.
* @author Ethan Cohen
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var errorMessage = '<iframe> elements must have a unique title property.';
var schema = (0, _schemas.generateObjSchema)();
module.exports = {
meta: {
docs: {},
schema: [schema]
},
create: function create(context) {
return {
JSXOpeningElement: function JSXOpeningElement(node) {
var type = (0, _jsxAstUtils.elementType)(node);
if (type && type !== 'iframe') {
return;
}
var title = (0, _jsxAstUtils.getPropValue)((0, _jsxAstUtils.getProp)(node.attributes, 'title'));
if (title && typeof title === 'string') {
return;
}
context.report({
node: node,
message: errorMessage
});
}
};
}
};

View File

@@ -0,0 +1,77 @@
'use strict';
var _jsxAstUtils = require('jsx-ast-utils');
var _schemas = require('../util/schemas');
var _isHiddenFromScreenReader = require('../util/isHiddenFromScreenReader');
var _isHiddenFromScreenReader2 = _interopRequireDefault(_isHiddenFromScreenReader);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var REDUNDANT_WORDS = ['image', 'photo', 'picture']; /**
* @fileoverview Enforce img alt attribute does not have the word image, picture, or photo.
* @author Ethan Cohen
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var errorMessage = 'Redundant alt attribute. Screen-readers already announce ' + '`img` tags as an image. You don\'t need to use the words `image`, ' + '`photo,` or `picture` (or any specified custom words) in the alt prop.';
var schema = (0, _schemas.generateObjSchema)({
components: _schemas.arraySchema,
words: _schemas.arraySchema
});
module.exports = {
meta: {
docs: {},
schema: [schema]
},
create: function create(context) {
return {
JSXOpeningElement: function JSXOpeningElement(node) {
var options = context.options[0] || {};
var componentOptions = options.components || [];
var typesToValidate = ['img'].concat(componentOptions);
var nodeType = (0, _jsxAstUtils.elementType)(node);
// Only check 'label' elements and custom types.
if (typesToValidate.indexOf(nodeType) === -1) {
return;
}
var altProp = (0, _jsxAstUtils.getProp)(node.attributes, 'alt');
// Return if alt prop is not present.
if (altProp === undefined) {
return;
}
var value = (0, _jsxAstUtils.getLiteralPropValue)(altProp);
var isVisible = (0, _isHiddenFromScreenReader2.default)(nodeType, node.attributes) === false;
var _options$words = options.words,
words = _options$words === undefined ? [] : _options$words;
var redundantWords = REDUNDANT_WORDS.concat(words);
if (typeof value === 'string' && isVisible) {
var hasRedundancy = redundantWords.some(function (word) {
return Boolean(value.match(new RegExp('(?!{)\\b' + word + '\\b(?!})', 'i')));
});
if (hasRedundancy === true) {
context.report({
node: node,
message: errorMessage
});
}
}
}
};
}
};

View File

@@ -0,0 +1,94 @@
'use strict';
var _ariaQuery = require('aria-query');
var _jsxAstUtils = require('jsx-ast-utils');
var _arrayIncludes = require('array-includes');
var _arrayIncludes2 = _interopRequireDefault(_arrayIncludes);
var _schemas = require('../util/schemas');
var _isHiddenFromScreenReader = require('../util/isHiddenFromScreenReader');
var _isHiddenFromScreenReader2 = _interopRequireDefault(_isHiddenFromScreenReader);
var _isInteractiveElement = require('../util/isInteractiveElement');
var _isInteractiveElement2 = _interopRequireDefault(_isInteractiveElement);
var _isInteractiveRole = require('../util/isInteractiveRole');
var _isInteractiveRole2 = _interopRequireDefault(_isInteractiveRole);
var _isNonInteractiveElement = require('../util/isNonInteractiveElement');
var _isNonInteractiveElement2 = _interopRequireDefault(_isNonInteractiveElement);
var _isNonInteractiveRole = require('../util/isNonInteractiveRole');
var _isNonInteractiveRole2 = _interopRequireDefault(_isNonInteractiveRole);
var _isPresentationRole = require('../util/isPresentationRole');
var _isPresentationRole2 = _interopRequireDefault(_isPresentationRole);
var _getTabIndex = require('../util/getTabIndex');
var _getTabIndex2 = _interopRequireDefault(_getTabIndex);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } /**
* @fileoverview Enforce that elements with onClick handlers must be focusable.
* @author Ethan Cohen
*
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var errorMessage = 'Elements with interactive roles must be focusable.';
var schema = (0, _schemas.generateObjSchema)();
var domElements = [].concat(_toConsumableArray(_ariaQuery.dom.keys()));
var interactiveProps = [].concat(_toConsumableArray(_jsxAstUtils.eventHandlersByType.mouse), _toConsumableArray(_jsxAstUtils.eventHandlersByType.keyboard));
module.exports = {
meta: {
docs: {},
schema: [schema]
},
create: function create(context) {
return {
JSXOpeningElement: function JSXOpeningElement(node) {
var attributes = node.attributes;
var type = (0, _jsxAstUtils.elementType)(node);
var hasInteractiveProps = (0, _jsxAstUtils.hasAnyProp)(attributes, interactiveProps);
var hasTabindex = (0, _getTabIndex2.default)((0, _jsxAstUtils.getProp)(attributes, 'tabIndex')) !== undefined;
if (!(0, _arrayIncludes2.default)(domElements, type)) {
// Do not test higher level JSX components, as we do not know what
// low-level DOM element this maps to.
return;
} else if (!hasInteractiveProps || (0, _isHiddenFromScreenReader2.default)(type, attributes) || (0, _isPresentationRole2.default)(type, attributes)) {
// Presentation is an intentional signal from the author that this
// element is not meant to be perceivable. For example, a click screen
// to close a dialog .
return;
}
if (hasInteractiveProps && (0, _isInteractiveRole2.default)(type, attributes) && !(0, _isInteractiveElement2.default)(type, attributes) && !(0, _isNonInteractiveElement2.default)(type, attributes) && !(0, _isNonInteractiveRole2.default)(type, attributes) && !hasTabindex) {
context.report({
node: node,
message: errorMessage
});
}
}
};
}
};

View File

@@ -0,0 +1,52 @@
'use strict';
var _jsxAstUtils = require('jsx-ast-utils');
var _schemas = require('../util/schemas');
/**
* @fileoverview Enforce label tags have htmlFor attribute.
* @author Ethan Cohen
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var errorMessage = 'Form controls using a label to identify them must be ' + 'programmatically associated with the control using htmlFor';
var schema = (0, _schemas.generateObjSchema)({ components: _schemas.arraySchema });
module.exports = {
meta: {
docs: {},
schema: [schema]
},
create: function create(context) {
return {
JSXOpeningElement: function JSXOpeningElement(node) {
var options = context.options[0] || {};
var componentOptions = options.components || [];
var typesToValidate = ['label'].concat(componentOptions);
var nodeType = (0, _jsxAstUtils.elementType)(node);
// Only check 'label' elements and custom types.
if (typesToValidate.indexOf(nodeType) === -1) {
return;
}
var htmlForAttr = (0, _jsxAstUtils.getProp)(node.attributes, 'htmlFor');
var htmlForValue = (0, _jsxAstUtils.getPropValue)(htmlForAttr);
var isInvalid = htmlForAttr === false || !htmlForValue;
if (isInvalid) {
context.report({
node: node,
message: errorMessage
});
}
}
};
}
};

View File

@@ -0,0 +1,74 @@
'use strict';
var _jsxAstUtils = require('jsx-ast-utils');
var _schemas = require('../util/schemas');
var _ISO = require('../util/attributes/ISO.json');
var _ISO2 = _interopRequireDefault(_ISO);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var errorMessage = 'lang attribute must have a valid value.'; /**
* @fileoverview Enforce lang attribute has a valid value.
* @author Ethan Cohen
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var schema = (0, _schemas.generateObjSchema)();
module.exports = {
meta: {
docs: {},
schema: [schema]
},
create: function create(context) {
return {
JSXAttribute: function JSXAttribute(node) {
var name = (0, _jsxAstUtils.propName)(node);
if (name && name.toUpperCase() !== 'LANG') {
return;
}
var parent = node.parent;
var type = (0, _jsxAstUtils.elementType)(parent);
if (type && type !== 'html') {
return;
}
var value = (0, _jsxAstUtils.getLiteralPropValue)(node);
// Don't check identifiers
if (value === null) {
return;
} else if (value === undefined) {
context.report({
node: node,
message: errorMessage
});
return;
}
var hyphen = value.indexOf('-');
var lang = hyphen > -1 ? value.substring(0, hyphen) : value;
var country = hyphen > -1 ? value.substring(3) : undefined;
if (_ISO2.default.languages.indexOf(lang) > -1 && (country === undefined || _ISO2.default.countries.indexOf(country) > -1)) {
return;
}
context.report({
node: node,
message: errorMessage
});
}
};
}
};

View File

@@ -0,0 +1,91 @@
'use strict';
var _jsxAstUtils = require('jsx-ast-utils');
var _schemas = require('../util/schemas');
var errorMessage = 'Media elements such as <audio> and <video> must have a <track> for captions.'; /**
* @fileoverview <audio> and <video> elements must have a <track> for captions.
* @author Ethan Cohen
*
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var MEDIA_TYPES = ['audio', 'video'];
var schema = (0, _schemas.generateObjSchema)({
audio: _schemas.arraySchema,
video: _schemas.arraySchema,
track: _schemas.arraySchema
});
var isMediaType = function isMediaType(context, type) {
var options = context.options[0] || {};
return MEDIA_TYPES.map(function (mediaType) {
return options[mediaType];
}).reduce(function (types, customComponent) {
return types.concat(customComponent);
}, MEDIA_TYPES).some(function (typeToCheck) {
return typeToCheck === type;
});
};
var isTrackType = function isTrackType(context, type) {
var options = context.options[0] || {};
return ['track'].concat(options.track || []).some(function (typeToCheck) {
return typeToCheck === type;
});
};
module.exports = {
meta: {
docs: {},
schema: [schema]
},
create: function create(context) {
return {
JSXElement: function JSXElement(node) {
var element = node.openingElement;
var type = (0, _jsxAstUtils.elementType)(element);
if (!isMediaType(context, type)) {
return;
}
// $FlowFixMe https://github.com/facebook/flow/issues/1414
var trackChildren = node.children.filter(function (child) {
if (child.type !== 'JSXElement') {
return false;
}
// $FlowFixMe https://github.com/facebook/flow/issues/1414
return isTrackType(context, (0, _jsxAstUtils.elementType)(child.openingElement));
});
if (trackChildren.length === 0) {
context.report({
node: element,
message: errorMessage
});
return;
}
var hasCaption = trackChildren.some(function (track) {
var kindProp = (0, _jsxAstUtils.getProp)(track.openingElement.attributes, 'kind');
var kindPropValue = (0, _jsxAstUtils.getLiteralPropValue)(kindProp) || '';
return kindPropValue.toLowerCase() === 'captions';
});
if (!hasCaption) {
context.report({
node: element,
message: errorMessage
});
}
}
};
}
};

View File

@@ -0,0 +1,66 @@
'use strict';
var _jsxAstUtils = require('jsx-ast-utils');
var _schemas = require('../util/schemas');
/**
* @fileoverview Enforce onmouseover/onmouseout are
* accompanied by onfocus/onblur.
* @author Ethan Cohen
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var mouseOverErrorMessage = 'onMouseOver must be accompanied by onFocus for accessibility.';
var mouseOutErrorMessage = 'onMouseOut must be accompanied by onBlur for accessibility.';
var schema = (0, _schemas.generateObjSchema)();
module.exports = {
meta: {
docs: {},
schema: [schema]
},
create: function create(context) {
return {
JSXOpeningElement: function JSXOpeningElement(node) {
var attributes = node.attributes;
// Check onmouseover / onfocus pairing.
var onMouseOver = (0, _jsxAstUtils.getProp)(attributes, 'onMouseOver');
var onMouseOverValue = (0, _jsxAstUtils.getPropValue)(onMouseOver);
if (onMouseOver && (onMouseOverValue !== null || onMouseOverValue !== undefined)) {
var hasOnFocus = (0, _jsxAstUtils.getProp)(attributes, 'onFocus');
var onFocusValue = (0, _jsxAstUtils.getPropValue)(hasOnFocus);
if (hasOnFocus === false || onFocusValue === null || onFocusValue === undefined) {
context.report({
node: node,
message: mouseOverErrorMessage
});
}
}
// Checkout onmouseout / onblur pairing
var onMouseOut = (0, _jsxAstUtils.getProp)(attributes, 'onMouseOut');
var onMouseOutValue = (0, _jsxAstUtils.getPropValue)(onMouseOut);
if (onMouseOut && (onMouseOutValue !== null || onMouseOutValue !== undefined)) {
var hasOnBlur = (0, _jsxAstUtils.getProp)(attributes, 'onBlur');
var onBlurValue = (0, _jsxAstUtils.getPropValue)(hasOnBlur);
if (hasOnBlur === false || onBlurValue === null || onBlurValue === undefined) {
context.report({
node: node,
message: mouseOutErrorMessage
});
}
}
}
};
}
};

View File

@@ -0,0 +1,41 @@
'use strict';
var _jsxAstUtils = require('jsx-ast-utils');
var _schemas = require('../util/schemas');
/**
* @fileoverview Enforce no accesskey attribute on element.
* @author Ethan Cohen
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var errorMessage = 'No access key attribute allowed. Inconsistencies ' + 'between keyboard shortcuts and keyboard comments used by screenreader ' + 'and keyboard only users create a11y complications.';
var schema = (0, _schemas.generateObjSchema)();
module.exports = {
meta: {
docs: {},
schema: [schema]
},
create: function create(context) {
return {
JSXOpeningElement: function JSXOpeningElement(node) {
var accessKey = (0, _jsxAstUtils.getProp)(node.attributes, 'accesskey');
var accessKeyValue = (0, _jsxAstUtils.getPropValue)(accessKey);
if (accessKey && accessKeyValue) {
context.report({
node: node,
message: errorMessage
});
}
}
};
}
};

View File

@@ -0,0 +1,56 @@
'use strict';
var _jsxAstUtils = require('jsx-ast-utils');
var _ariaQuery = require('aria-query');
var _schemas = require('../util/schemas');
var errorMessage = 'The autoFocus prop should not be used, as it can reduce usability and accessibility for users.'; /**
* @fileoverview Enforce autoFocus prop is not used.
* @author Ethan Cohen <@evcohen>
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var schema = (0, _schemas.generateObjSchema)({
ignoreNonDOM: {
type: 'boolean',
default: false
}
});
module.exports = {
meta: {
docs: {},
schema: [schema]
},
create: function create(context) {
return {
JSXAttribute: function JSXAttribute(attribute) {
// Determine if ignoreNonDOM is set to true
// If true, then do not run rule.
var options = context.options[0] || {};
var ignoreNonDOM = !!options.ignoreNonDOM;
if (ignoreNonDOM) {
var type = (0, _jsxAstUtils.elementType)(attribute.parent);
if (!_ariaQuery.dom.get(type)) {
return;
}
}
// Don't normalize, since React only recognizes autoFocus on low-level DOM elements.
if ((0, _jsxAstUtils.propName)(attribute) === 'autoFocus') {
context.report({
node: attribute,
message: errorMessage
});
}
}
};
}
};

View File

@@ -0,0 +1,51 @@
'use strict';
var _jsxAstUtils = require('jsx-ast-utils');
var _schemas = require('../util/schemas');
/**
* @fileoverview Enforce distracting elements are not used.
* @author Ethan Cohen
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var errorMessage = function errorMessage(element) {
return 'Do not use <' + element + '> elements as they can create visual accessibility issues and are deprecated.';
};
var DEFAULT_ELEMENTS = ['marquee', 'blink'];
var schema = (0, _schemas.generateObjSchema)({
elements: (0, _schemas.enumArraySchema)(DEFAULT_ELEMENTS)
});
module.exports = {
meta: {
docs: {},
schema: [schema]
},
create: function create(context) {
return {
JSXOpeningElement: function JSXOpeningElement(node) {
var options = context.options[0] || {};
var elementOptions = options.elements || DEFAULT_ELEMENTS;
var type = (0, _jsxAstUtils.elementType)(node);
var distractingElement = elementOptions.find(function (element) {
return type === element;
});
if (distractingElement) {
context.report({
node: node,
message: errorMessage(distractingElement)
});
}
}
};
}
};

View File

@@ -0,0 +1,89 @@
'use strict';
var _ariaQuery = require('aria-query');
var _jsxAstUtils = require('jsx-ast-utils');
var _arrayIncludes = require('array-includes');
var _arrayIncludes2 = _interopRequireDefault(_arrayIncludes);
var _isInteractiveElement = require('../util/isInteractiveElement');
var _isInteractiveElement2 = _interopRequireDefault(_isInteractiveElement);
var _isNonInteractiveRole = require('../util/isNonInteractiveRole');
var _isNonInteractiveRole2 = _interopRequireDefault(_isNonInteractiveRole);
var _isPresentationRole = require('../util/isPresentationRole');
var _isPresentationRole2 = _interopRequireDefault(_isPresentationRole);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } /**
* @fileoverview Disallow inherently interactive elements to be assigned
* non-interactive roles.
* @author Jesse Beach
*
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var errorMessage = 'Interactive elements should not be assigned non-interactive roles.';
var domElements = [].concat(_toConsumableArray(_ariaQuery.dom.keys()));
module.exports = {
meta: {
docs: {},
schema: [{
type: 'object',
additionalProperties: {
type: 'array',
items: {
type: 'string'
},
uniqueItems: true
}
}]
},
create: function create(context) {
var options = context.options;
return {
JSXAttribute: function JSXAttribute(attribute) {
var attributeName = (0, _jsxAstUtils.propName)(attribute);
if (attributeName !== 'role') {
return;
}
var node = attribute.parent;
var attributes = node.attributes;
var type = (0, _jsxAstUtils.elementType)(node);
var role = (0, _jsxAstUtils.getLiteralPropValue)((0, _jsxAstUtils.getProp)(node.attributes, 'role'));
if (!(0, _arrayIncludes2.default)(domElements, type)) {
// Do not test higher level JSX components, as we do not know what
// low-level DOM element this maps to.
return;
}
// Allow overrides from rule configuration for specific elements and
// roles.
var allowedRoles = options[0] || {};
if (Object.prototype.hasOwnProperty.call(allowedRoles, type) && (0, _arrayIncludes2.default)(allowedRoles[type], role)) {
return;
}
if ((0, _isInteractiveElement2.default)(type, attributes) && ((0, _isNonInteractiveRole2.default)(type, attributes) || (0, _isPresentationRole2.default)(type, attributes))) {
// Visible, non-interactive elements should not have an interactive handler.
context.report({
node: attribute,
message: errorMessage
});
}
}
};
}
};

View File

@@ -0,0 +1,100 @@
'use strict';
var _ariaQuery = require('aria-query');
var _jsxAstUtils = require('jsx-ast-utils');
var _arrayIncludes = require('array-includes');
var _arrayIncludes2 = _interopRequireDefault(_arrayIncludes);
var _schemas = require('../util/schemas');
var _isAbstractRole = require('../util/isAbstractRole');
var _isAbstractRole2 = _interopRequireDefault(_isAbstractRole);
var _isHiddenFromScreenReader = require('../util/isHiddenFromScreenReader');
var _isHiddenFromScreenReader2 = _interopRequireDefault(_isHiddenFromScreenReader);
var _isInteractiveElement = require('../util/isInteractiveElement');
var _isInteractiveElement2 = _interopRequireDefault(_isInteractiveElement);
var _isInteractiveRole = require('../util/isInteractiveRole');
var _isInteractiveRole2 = _interopRequireDefault(_isInteractiveRole);
var _isNonInteractiveElement = require('../util/isNonInteractiveElement');
var _isNonInteractiveElement2 = _interopRequireDefault(_isNonInteractiveElement);
var _isNonInteractiveRole = require('../util/isNonInteractiveRole');
var _isNonInteractiveRole2 = _interopRequireDefault(_isNonInteractiveRole);
var _isPresentationRole = require('../util/isPresentationRole');
var _isPresentationRole2 = _interopRequireDefault(_isPresentationRole);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } /**
* @fileoverview Enforce non-interactive elements have no interactive handlers.
* @author Jese Beach
*
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var errorMessage = 'Non-interactive elements should not be assigned mouse or keyboard event listeners.';
var domElements = [].concat(_toConsumableArray(_ariaQuery.dom.keys()));
var defaultInteractiveProps = _jsxAstUtils.eventHandlers;
var schema = (0, _schemas.generateObjSchema)({
handlers: _schemas.arraySchema
});
module.exports = {
meta: {
docs: {},
schema: [schema]
},
create: function create(context) {
var options = context.options;
return {
JSXOpeningElement: function JSXOpeningElement(node) {
var attributes = node.attributes;
var type = (0, _jsxAstUtils.elementType)(node);
var interactiveProps = options[0] ? options[0].handlers : defaultInteractiveProps;
var hasInteractiveProps = interactiveProps.some(function (prop) {
return (0, _jsxAstUtils.hasProp)(attributes, prop) && (0, _jsxAstUtils.getPropValue)((0, _jsxAstUtils.getProp)(attributes, prop)) != null;
});
if (!(0, _arrayIncludes2.default)(domElements, type)) {
// Do not test higher level JSX components, as we do not know what
// low-level DOM element this maps to.
return;
} else if (!hasInteractiveProps || (0, _isHiddenFromScreenReader2.default)(type, attributes) || (0, _isPresentationRole2.default)(type, attributes)) {
// Presentation is an intentional signal from the author that this
// element is not meant to be perceivable. For example, a click screen
// to close a dialog .
return;
} else if ((0, _isInteractiveElement2.default)(type, attributes) || (0, _isInteractiveRole2.default)(type, attributes) || !(0, _isNonInteractiveElement2.default)(type, attributes) && !(0, _isNonInteractiveRole2.default)(type, attributes) || (0, _isAbstractRole2.default)(type, attributes)) {
// This rule has no opinion about abtract roles.
return;
}
// Visible, non-interactive elements should not have an interactive handler.
context.report({
node: node,
message: errorMessage
});
}
};
}
};

View File

@@ -0,0 +1,84 @@
'use strict';
var _ariaQuery = require('aria-query');
var _jsxAstUtils = require('jsx-ast-utils');
var _arrayIncludes = require('array-includes');
var _arrayIncludes2 = _interopRequireDefault(_arrayIncludes);
var _isNonInteractiveElement = require('../util/isNonInteractiveElement');
var _isNonInteractiveElement2 = _interopRequireDefault(_isNonInteractiveElement);
var _isInteractiveRole = require('../util/isInteractiveRole');
var _isInteractiveRole2 = _interopRequireDefault(_isInteractiveRole);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } /**
* @fileoverview Disallow inherently non-interactive elements to be assigned
* interactive roles.
* @author Jesse Beach
*
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var errorMessage = 'Non-interactive elements should not be assigned interactive roles.';
var domElements = [].concat(_toConsumableArray(_ariaQuery.dom.keys()));
module.exports = {
meta: {
docs: {},
schema: [{
type: 'object',
additionalProperties: {
type: 'array',
items: {
type: 'string'
},
uniqueItems: true
}
}]
},
create: function create(context) {
var options = context.options;
return {
JSXAttribute: function JSXAttribute(attribute) {
var attributeName = (0, _jsxAstUtils.propName)(attribute);
if (attributeName !== 'role') {
return;
}
var node = attribute.parent;
var attributes = node.attributes;
var type = (0, _jsxAstUtils.elementType)(node);
var role = (0, _jsxAstUtils.getLiteralPropValue)((0, _jsxAstUtils.getProp)(node.attributes, 'role'));
if (!(0, _arrayIncludes2.default)(domElements, type)) {
// Do not test higher level JSX components, as we do not know what
// low-level DOM element this maps to.
return;
}
// Allow overrides from rule configuration for specific elements and
// roles.
var allowedRoles = options[0] || {};
if (Object.prototype.hasOwnProperty.call(allowedRoles, type) && (0, _arrayIncludes2.default)(allowedRoles[type], role)) {
return;
}
if ((0, _isNonInteractiveElement2.default)(type, attributes) && (0, _isInteractiveRole2.default)(type, attributes)) {
context.report({
node: attribute,
message: errorMessage
});
}
}
};
}
};

View File

@@ -0,0 +1,94 @@
'use strict';
var _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; /**
* @fileoverview Disallow tabindex on static and noninteractive elements
* @author jessebeach
*
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var _ariaQuery = require('aria-query');
var _jsxAstUtils = require('jsx-ast-utils');
var _arrayIncludes = require('array-includes');
var _arrayIncludes2 = _interopRequireDefault(_arrayIncludes);
var _isInteractiveElement = require('../util/isInteractiveElement');
var _isInteractiveElement2 = _interopRequireDefault(_isInteractiveElement);
var _isInteractiveRole = require('../util/isInteractiveRole');
var _isInteractiveRole2 = _interopRequireDefault(_isInteractiveRole);
var _schemas = require('../util/schemas');
var _getTabIndex = require('../util/getTabIndex');
var _getTabIndex2 = _interopRequireDefault(_getTabIndex);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var errorMessage = '`tabIndex` should only be declared on interactive elements.';
var schema = (0, _schemas.generateObjSchema)({
roles: _extends({}, _schemas.arraySchema, {
description: 'An array of ARIA roles'
}),
tags: _extends({}, _schemas.arraySchema, {
description: 'An array of HTML tag names'
})
});
module.exports = {
meta: {
docs: {},
schema: [schema]
},
create: function create(context) {
var options = context.options;
return {
JSXOpeningElement: function JSXOpeningElement(node) {
var type = (0, _jsxAstUtils.elementType)(node);
var attributes = node.attributes;
var tabIndexProp = (0, _jsxAstUtils.getProp)(attributes, 'tabIndex');
var tabIndex = (0, _getTabIndex2.default)(tabIndexProp);
// Early return;
if (typeof tabIndex === 'undefined') {
return;
}
var role = (0, _jsxAstUtils.getLiteralPropValue)((0, _jsxAstUtils.getProp)(node.attributes, 'role'));
if (!_ariaQuery.dom.has(type)) {
// Do not test higher level JSX components, as we do not know what
// low-level DOM element this maps to.
return;
}
// Allow for configuration overrides.
var _ref = options[0] || {},
tags = _ref.tags,
roles = _ref.roles;
if (tags && (0, _arrayIncludes2.default)(tags, type) || roles && (0, _arrayIncludes2.default)(roles, role)) {
return;
}
if ((0, _isInteractiveElement2.default)(type, attributes) || (0, _isInteractiveRole2.default)(type, attributes)) {
return;
}
if (tabIndex >= 0) {
context.report({
node: tabIndexProp,
message: errorMessage
});
}
}
};
}
};

View File

@@ -0,0 +1,49 @@
'use strict';
var _jsxAstUtils = require('jsx-ast-utils');
var _schemas = require('../util/schemas');
/**
* @fileoverview Enforce usage of onBlur over onChange for accessibility.
* @author Ethan Cohen
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var errorMessage = 'onBlur must be used instead of onchange, ' + 'unless absolutely necessary and it causes no negative consequences ' + 'for keyboard only or screen reader users.';
var applicableTypes = ['select', 'option'];
var schema = (0, _schemas.generateObjSchema)();
module.exports = {
meta: {
docs: {},
schema: [schema]
},
create: function create(context) {
return {
JSXOpeningElement: function JSXOpeningElement(node) {
var nodeType = (0, _jsxAstUtils.elementType)(node);
if (applicableTypes.indexOf(nodeType) === -1) {
return;
}
var onChange = (0, _jsxAstUtils.getProp)(node.attributes, 'onChange');
var hasOnBlur = (0, _jsxAstUtils.getProp)(node.attributes, 'onBlur') !== undefined;
if (onChange && !hasOnBlur) {
context.report({
node: node,
message: errorMessage
});
}
}
};
}
};

View File

@@ -0,0 +1,55 @@
'use strict';
var _jsxAstUtils = require('jsx-ast-utils');
var _schemas = require('../util/schemas');
var _getImplicitRole = require('../util/getImplicitRole');
var _getImplicitRole2 = _interopRequireDefault(_getImplicitRole);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var errorMessage = function errorMessage(element, implicitRole) {
return 'The element ' + element + ' has an implicit role of ' + implicitRole + '. Defining this explicitly is redundant and should be avoided.';
}; /**
* @fileoverview Enforce explicit role property is not the
* same as implicit/default role property on element.
* @author Ethan Cohen <@evcohen>
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var schema = (0, _schemas.generateObjSchema)();
module.exports = {
meta: {
docs: {},
schema: [schema]
},
create: function create(context) {
return {
JSXOpeningElement: function JSXOpeningElement(node) {
var type = (0, _jsxAstUtils.elementType)(node);
var implicitRole = (0, _getImplicitRole2.default)(type, node.attributes);
if (implicitRole === '') {
return;
}
var role = (0, _jsxAstUtils.getProp)(node.attributes, 'role');
var roleValue = (0, _jsxAstUtils.getLiteralPropValue)(role);
if (typeof roleValue === 'string' && roleValue.toUpperCase() === implicitRole.toUpperCase()) {
context.report({
node: node,
message: errorMessage(type, implicitRole.toLowerCase())
});
}
}
};
}
};

View File

@@ -0,0 +1,101 @@
'use strict';
var _ariaQuery = require('aria-query');
var _jsxAstUtils = require('jsx-ast-utils');
var _arrayIncludes = require('array-includes');
var _arrayIncludes2 = _interopRequireDefault(_arrayIncludes);
var _schemas = require('../util/schemas');
var _isAbstractRole = require('../util/isAbstractRole');
var _isAbstractRole2 = _interopRequireDefault(_isAbstractRole);
var _isHiddenFromScreenReader = require('../util/isHiddenFromScreenReader');
var _isHiddenFromScreenReader2 = _interopRequireDefault(_isHiddenFromScreenReader);
var _isInteractiveElement = require('../util/isInteractiveElement');
var _isInteractiveElement2 = _interopRequireDefault(_isInteractiveElement);
var _isInteractiveRole = require('../util/isInteractiveRole');
var _isInteractiveRole2 = _interopRequireDefault(_isInteractiveRole);
var _isNonInteractiveElement = require('../util/isNonInteractiveElement');
var _isNonInteractiveElement2 = _interopRequireDefault(_isNonInteractiveElement);
var _isNonInteractiveRole = require('../util/isNonInteractiveRole');
var _isNonInteractiveRole2 = _interopRequireDefault(_isNonInteractiveRole);
var _isPresentationRole = require('../util/isPresentationRole');
var _isPresentationRole2 = _interopRequireDefault(_isPresentationRole);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } /**
* @fileoverview Enforce static elements have no interactive handlers.
* @author Ethan Cohen
*
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var errorMessage = 'Static HTML elements with event handlers require a role.';
var domElements = [].concat(_toConsumableArray(_ariaQuery.dom.keys()));
var defaultInteractiveProps = _jsxAstUtils.eventHandlers;
var schema = (0, _schemas.generateObjSchema)({
handlers: _schemas.arraySchema
});
module.exports = {
meta: {
docs: {},
schema: [schema]
},
create: function create(context) {
var options = context.options;
return {
JSXOpeningElement: function JSXOpeningElement(node) {
var attributes = node.attributes;
var type = (0, _jsxAstUtils.elementType)(node);
var interactiveProps = options[0] ? options[0].handlers : defaultInteractiveProps;
var hasInteractiveProps = interactiveProps.some(function (prop) {
return (0, _jsxAstUtils.hasProp)(attributes, prop) && (0, _jsxAstUtils.getPropValue)((0, _jsxAstUtils.getProp)(attributes, prop)) != null;
});
if (!(0, _arrayIncludes2.default)(domElements, type)) {
// Do not test higher level JSX components, as we do not know what
// low-level DOM element this maps to.
return;
} else if (!hasInteractiveProps || (0, _isHiddenFromScreenReader2.default)(type, attributes) || (0, _isPresentationRole2.default)(type, attributes)) {
// Presentation is an intentional signal from the author that this
// element is not meant to be perceivable. For example, a click screen
// to close a dialog .
return;
} else if ((0, _isInteractiveElement2.default)(type, attributes) || (0, _isInteractiveRole2.default)(type, attributes) || (0, _isNonInteractiveElement2.default)(type, attributes) || (0, _isNonInteractiveRole2.default)(type, attributes) || (0, _isAbstractRole2.default)(type, attributes)) {
// This rule has no opinion about abstract roles.
return;
}
// Visible, non-interactive elements should not have an interactive handler.
context.report({
node: node,
message: errorMessage
});
}
};
}
};

View File

@@ -0,0 +1,76 @@
'use strict';
var _ariaQuery = require('aria-query');
var _jsxAstUtils = require('jsx-ast-utils');
var _schemas = require('../util/schemas');
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } /**
* @fileoverview Enforce that elements with ARIA roles must
* have all required attributes for that role.
* @author Ethan Cohen
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var errorMessage = function errorMessage(role, requiredProps) {
return 'Elements with the ARIA role "' + role + '" must have the following ' + ('attributes defined: ' + String(requiredProps).toLowerCase());
};
var schema = (0, _schemas.generateObjSchema)();
module.exports = {
meta: {
docs: {},
schema: [schema]
},
create: function create(context) {
return {
JSXAttribute: function JSXAttribute(attribute) {
var name = (0, _jsxAstUtils.propName)(attribute);
var normalizedName = name ? name.toLowerCase() : '';
if (normalizedName !== 'role') {
return;
}
var value = (0, _jsxAstUtils.getLiteralPropValue)(attribute);
// If value is undefined, then the role attribute will be dropped in the DOM.
// If value is null, then getLiteralAttributeValue is telling us
// that the value isn't in the form of a literal.
if (value === undefined || value === null) {
return;
}
var normalizedValues = String(value).toLowerCase().split(' ');
var validRoles = normalizedValues.filter(function (val) {
return [].concat(_toConsumableArray(_ariaQuery.roles.keys())).indexOf(val) > -1;
});
validRoles.forEach(function (role) {
var _roles$get = _ariaQuery.roles.get(role),
requiredPropKeyValues = _roles$get.requiredProps;
var requiredProps = Object.keys(requiredPropKeyValues);
if (requiredProps.length > 0) {
var hasRequiredProps = requiredProps.every(function (prop) {
return (0, _jsxAstUtils.getProp)(attribute.parent.attributes, prop);
});
if (hasRequiredProps === false) {
context.report({
node: attribute,
message: errorMessage(role.toLowerCase(), requiredProps)
});
}
}
});
}
};
}
};

View File

@@ -0,0 +1,83 @@
'use strict';
var _ariaQuery = require('aria-query');
var _jsxAstUtils = require('jsx-ast-utils');
var _schemas = require('../util/schemas');
var _getImplicitRole = require('../util/getImplicitRole');
var _getImplicitRole2 = _interopRequireDefault(_getImplicitRole);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } /**
* @fileoverview Enforce that elements with explicit or implicit roles defined contain only
* `aria-*` properties supported by that `role`.
* @author Ethan Cohen
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var errorMessage = function errorMessage(attr, role, tag, isImplicit) {
if (isImplicit) {
return 'The attribute ' + attr + ' is not supported by the role ' + role + '. This role is implicit on the element ' + tag + '.';
}
return 'The attribute ' + attr + ' is not supported by the role ' + role + '.';
};
var schema = (0, _schemas.generateObjSchema)();
module.exports = {
meta: {
docs: {},
schema: [schema]
},
create: function create(context) {
return {
JSXOpeningElement: function JSXOpeningElement(node) {
// If role is not explicitly defined, then try and get its implicit role.
var type = (0, _jsxAstUtils.elementType)(node);
var role = (0, _jsxAstUtils.getProp)(node.attributes, 'role');
var roleValue = role ? (0, _jsxAstUtils.getLiteralPropValue)(role) : (0, _getImplicitRole2.default)(type, node.attributes);
var isImplicit = roleValue && role === undefined;
// If there is no explicit or implicit role, then assume that the element
// can handle the global set of aria-* properties.
// This actually isn't true - should fix in future release.
if (typeof roleValue !== 'string' || _ariaQuery.roles.get(roleValue) === undefined) {
return;
}
// Make sure it has no aria-* properties defined outside of its property set.
var _roles$get = _ariaQuery.roles.get(roleValue),
propKeyValues = _roles$get.props;
var propertySet = Object.keys(propKeyValues);
var invalidAriaPropsForRole = [].concat(_toConsumableArray(_ariaQuery.aria.keys())).filter(function (attribute) {
return propertySet.indexOf(attribute) === -1;
});
node.attributes.forEach(function (prop) {
if (prop.type === 'JSXSpreadAttribute') {
return;
}
var name = (0, _jsxAstUtils.propName)(prop);
if (invalidAriaPropsForRole.indexOf(name) > -1) {
context.report({
node: node,
message: errorMessage(name, roleValue, type, isImplicit)
});
}
});
}
};
}
};

View File

@@ -0,0 +1,53 @@
'use strict';
var _ariaQuery = require('aria-query');
var _jsxAstUtils = require('jsx-ast-utils');
var _schemas = require('../util/schemas');
var errorMessage = 'The scope prop can only be used on <th> elements.'; /**
* @fileoverview Enforce scope prop is only used on <th> elements.
* @author Ethan Cohen
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var schema = (0, _schemas.generateObjSchema)();
module.exports = {
meta: {
docs: {},
schema: [schema]
},
create: function create(context) {
return {
JSXAttribute: function JSXAttribute(node) {
var name = (0, _jsxAstUtils.propName)(node);
if (name && name.toUpperCase() !== 'SCOPE') {
return;
}
var parent = node.parent;
var tagName = (0, _jsxAstUtils.elementType)(parent);
// Do not test higher level JSX components, as we do not know what
// low-level DOM element this maps to.
if (!_ariaQuery.dom.has(tagName)) {
return;
} else if (tagName && tagName.toUpperCase() === 'TH') {
return;
}
context.report({
node: node,
message: errorMessage
});
}
};
}
};

View File

@@ -0,0 +1,51 @@
'use strict';
var _jsxAstUtils = require('jsx-ast-utils');
var _schemas = require('../util/schemas');
/**
* @fileoverview Enforce tabIndex value is not greater than zero.
* @author Ethan Cohen
*/
// ----------------------------------------------------------------------------
// Rule Definition
// ----------------------------------------------------------------------------
var errorMessage = 'Avoid positive integer values for tabIndex.';
var schema = (0, _schemas.generateObjSchema)();
module.exports = {
meta: {
docs: {},
schema: [schema]
},
create: function create(context) {
return {
JSXAttribute: function JSXAttribute(attribute) {
var name = (0, _jsxAstUtils.propName)(attribute);
var normalizedName = name ? name.toUpperCase() : '';
// Check if tabIndex is the attribute
if (normalizedName !== 'TABINDEX') {
return;
}
// Only check literals because we can't infer values from certain expressions.
var value = Number((0, _jsxAstUtils.getLiteralPropValue)(attribute));
if (isNaN(value) || value <= 0) {
return;
}
context.report({
node: attribute,
message: errorMessage
});
}
};
}
};

View File

@@ -0,0 +1,390 @@
{
"countries": [
"AF",
"AL",
"DZ",
"AS",
"AD",
"AO",
"AI",
"AQ",
"AG",
"AR",
"AM",
"AW",
"AU",
"AT",
"AZ",
"BS",
"BH",
"BD",
"BB",
"BY",
"BE",
"BZ",
"BJ",
"BM",
"BT",
"BO",
"BA",
"BW",
"BR",
"IO",
"VG",
"BN",
"BG",
"BF",
"MM",
"BI",
"KH",
"CM",
"CA",
"CV",
"KY",
"CF",
"TD",
"CL",
"CN",
"CX",
"CC",
"CO",
"KM",
"CK",
"CR",
"HR",
"CU",
"CY",
"CZ",
"CD",
"DK",
"DJ",
"DM",
"DO",
"EC",
"EG",
"SV",
"GQ",
"ER",
"EE",
"ET",
"FK",
"FO",
"FJ",
"FI",
"FR",
"PF",
"GA",
"GM",
"GE",
"DE",
"GH",
"GI",
"GR",
"GL",
"GD",
"GU",
"GT",
"GN",
"GW",
"GY",
"HT",
"VA",
"HN",
"HK",
"HU",
"IS",
"IN",
"ID",
"IR",
"IQ",
"IE",
"IM",
"IL",
"IT",
"CI",
"JM",
"JP",
"JE",
"JO",
"KZ",
"KE",
"KI",
"KW",
"KG",
"LA",
"LV",
"LB",
"LS",
"LR",
"LY",
"LI",
"LT",
"LU",
"MO",
"MK",
"MG",
"MW",
"MY",
"MV",
"ML",
"MT",
"MH",
"MR",
"MU",
"YT",
"MX",
"FM",
"MD",
"MC",
"MN",
"ME",
"MS",
"MA",
"MZ",
"NA",
"NR",
"NP",
"NL",
"AN",
"NC",
"NZ",
"NI",
"NE",
"NG",
"NU",
"KP",
"MP",
"NO",
"OM",
"PK",
"PW",
"PA",
"PG",
"PY",
"PE",
"PH",
"PN",
"PL",
"PT",
"PR",
"QA",
"CG",
"RO",
"RU",
"RW",
"BL",
"SH",
"KN",
"LC",
"MF",
"PM",
"VC",
"WS",
"SM",
"ST",
"SA",
"SN",
"RS",
"SC",
"SL",
"SG",
"SK",
"SI",
"SB",
"SO",
"ZA",
"KR",
"ES",
"LK",
"SD",
"SR",
"SJ",
"SZ",
"SE",
"CH",
"SY",
"TW",
"TJ",
"TZ",
"TH",
"TL",
"TG",
"TK",
"TO",
"TT",
"TN",
"TR",
"TM",
"TC",
"TV",
"UG",
"UA",
"AE",
"GB",
"US",
"UY",
"VI",
"UZ",
"VU",
"VE",
"VN",
"WF",
"EH",
"YE",
"ZM",
"ZW"
],
"languages": [
"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"
]
}

View File

@@ -0,0 +1,36 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _jsxAstUtils = require('jsx-ast-utils');
/**
* Returns true if all items in baseAttributes are found in attributes. Always
* returns true if baseAttributes is empty.
*/
function attributesComparator() {
var baseAttributes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
var attributes = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
return baseAttributes.every(function (baseAttr) {
return attributes.some(function (attribute) {
// Guard against non-JSXAttribute nodes like JSXSpreadAttribute
if (attribute.type !== 'JSXAttribute') {
return false;
}
// Attribute matches.
if (baseAttr.name !== (0, _jsxAstUtils.propName)(attribute)) {
return false;
}
// Value exists and does not match.
if (baseAttr.value && baseAttr.value !== (0, _jsxAstUtils.getLiteralPropValue)(attribute)) {
return false;
}
return true;
});
});
}
exports.default = attributesComparator;

View File

@@ -0,0 +1,28 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getImplicitRole;
var _implicitRoles = require('./implicitRoles');
var _implicitRoles2 = _interopRequireDefault(_implicitRoles);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Returns an element's implicit role given its attributes and type.
* Some elements only have an implicit role when certain props are defined.
*
* @param type - The node's tagName.
* @param attributes - The collection of attributes on the node.
* @returns {String} - String representing the node's implicit role or '' if it doesn't exist.
*/
function getImplicitRole(type, attributes) {
if (_implicitRoles2.default[type]) {
return _implicitRoles2.default[type](attributes);
}
return '';
}

View File

@@ -0,0 +1,39 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getSuggestion;
var _damerauLevenshtein = require('damerau-levenshtein');
var _damerauLevenshtein2 = _interopRequireDefault(_damerauLevenshtein);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// Minimum edit distance to be considered a good suggestion.
var THRESHOLD = 2;
/**
* Returns an array of suggestions given a word and a dictionary and limit of suggestions
* to return.
*/
function getSuggestion(word) {
var dictionary = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
var limit = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 2;
var distances = dictionary.reduce(function (suggestions, dictionaryWord) {
var distance = (0, _damerauLevenshtein2.default)(word.toUpperCase(), dictionaryWord.toUpperCase());
var steps = distance.steps;
suggestions[dictionaryWord] = steps; // eslint-disable-line
return suggestions;
}, {});
return Object.keys(distances).filter(function (suggestion) {
return distances[suggestion] <= THRESHOLD;
}).sort(function (a, b) {
return distances[a] - distances[b];
}) // Sort by distance
.slice(0, limit);
}

View File

@@ -0,0 +1,39 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
exports.default = getTabIndex;
var _jsxAstUtils = require('jsx-ast-utils');
/**
* Returns the tabIndex value.
*/
function getTabIndex(tabIndex) {
var literalValue = (0, _jsxAstUtils.getLiteralPropValue)(tabIndex);
// String and number values.
if (['string', 'number'].indexOf(typeof literalValue === 'undefined' ? 'undefined' : _typeof(literalValue)) > -1) {
// Empty string will convert to zero, so check for it explicity.
if (typeof literalValue === 'string' && literalValue.length === 0) {
return undefined;
}
var value = Number(literalValue);
if (Number.isNaN(value)) {
return undefined;
}
return Number.isInteger(value) ? value : undefined;
}
// Booleans are not valid values, return undefined.
if (literalValue === true || literalValue === false) {
return undefined;
}
return (0, _jsxAstUtils.getPropValue)(tabIndex);
}

View File

@@ -0,0 +1,32 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = hasAccessibleChild;
var _jsxAstUtils = require('jsx-ast-utils');
var _isHiddenFromScreenReader = require('./isHiddenFromScreenReader');
var _isHiddenFromScreenReader2 = _interopRequireDefault(_isHiddenFromScreenReader);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function hasAccessibleChild(node) {
return node.children.some(function (child) {
switch (child.type) {
case 'Literal':
return Boolean(child.value);
case 'JSXElement':
return !(0, _isHiddenFromScreenReader2.default)((0, _jsxAstUtils.elementType)(child.openingElement), child.openingElement.attributes);
case 'JSXExpressionContainer':
if (child.expression.type === 'Identifier') {
return child.expression.name !== 'undefined';
}
return true;
default:
return false;
}
}) || (0, _jsxAstUtils.hasAnyProp)(node.openingElement.attributes, ['dangerouslySetInnerHTML', 'children']);
}

View File

@@ -0,0 +1,19 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getImplicitRoleForAnchor;
var _jsxAstUtils = require('jsx-ast-utils');
/**
* Returns the implicit role for an anchor tag.
*/
function getImplicitRoleForAnchor(attributes) {
if ((0, _jsxAstUtils.getProp)(attributes, 'href')) {
return 'link';
}
return '';
}

View File

@@ -0,0 +1,19 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getImplicitRoleForArea;
var _jsxAstUtils = require('jsx-ast-utils');
/**
* Returns the implicit role for an area tag.
*/
function getImplicitRoleForArea(attributes) {
if ((0, _jsxAstUtils.getProp)(attributes, 'href')) {
return 'link';
}
return '';
}

View File

@@ -0,0 +1,12 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getImplicitRoleForArticle;
/**
* Returns the implicit role for an article tag.
*/
function getImplicitRoleForArticle() {
return 'article';
}

View File

@@ -0,0 +1,12 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getImplicitRoleForAside;
/**
* Returns the implicit role for an aside tag.
*/
function getImplicitRoleForAside() {
return 'complementary';
}

View File

@@ -0,0 +1,12 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getImplicitRoleForBody;
/**
* Returns the implicit role for a body tag.
*/
function getImplicitRoleForBody() {
return 'document';
}

View File

@@ -0,0 +1,12 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getImplicitRoleForButton;
/**
* Returns the implicit role for a button tag.
*/
function getImplicitRoleForButton() {
return 'button';
}

View File

@@ -0,0 +1,12 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getImplicitRoleForDatalist;
/**
* Returns the implicit role for a datalist tag.
*/
function getImplicitRoleForDatalist() {
return 'listbox';
}

View File

@@ -0,0 +1,12 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getImplicitRoleForDetails;
/**
* Returns the implicit role for a details tag.
*/
function getImplicitRoleForDetails() {
return 'group';
}

View File

@@ -0,0 +1,12 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getImplicitRoleForDialog;
/**
* Returns the implicit role for a dialog tag.
*/
function getImplicitRoleForDialog() {
return 'dialog';
}

View File

@@ -0,0 +1,12 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getImplicitRoleForDl;
/**
* Returns the implicit role for a dl tag.
*/
function getImplicitRoleForDl() {
return 'list';
}

View File

@@ -0,0 +1,12 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getImplicitRoleForForm;
/**
* Returns the implicit role for a form tag.
*/
function getImplicitRoleForForm() {
return 'form';
}

View File

@@ -0,0 +1,12 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getImplicitRoleForH1;
/**
* Returns the implicit role for an h1 tag.
*/
function getImplicitRoleForH1() {
return 'heading';
}

View File

@@ -0,0 +1,12 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getImplicitRoleForH2;
/**
* Returns the implicit role for an h2 tag.
*/
function getImplicitRoleForH2() {
return 'heading';
}

View File

@@ -0,0 +1,12 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getImplicitRoleForH3;
/**
* Returns the implicit role for an h3 tag.
*/
function getImplicitRoleForH3() {
return 'heading';
}

View File

@@ -0,0 +1,12 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getImplicitRoleForH4;
/**
* Returns the implicit role for an h4 tag.
*/
function getImplicitRoleForH4() {
return 'heading';
}

View File

@@ -0,0 +1,12 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getImplicitRoleForH5;
/**
* Returns the implicit role for an h5 tag.
*/
function getImplicitRoleForH5() {
return 'heading';
}

View File

@@ -0,0 +1,12 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getImplicitRoleForH6;
/**
* Returns the implicit role for an h6tag.
*/
function getImplicitRoleForH6() {
return 'heading';
}

View File

@@ -0,0 +1,12 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getImplicitRoleForHr;
/**
* Returns the implicit role for an hr tag.
*/
function getImplicitRoleForHr() {
return 'separator';
}

View File

@@ -0,0 +1,21 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getImplicitRoleForImg;
var _jsxAstUtils = require('jsx-ast-utils');
/**
* Returns the implicit role for an img tag.
*/
function getImplicitRoleForImg(attributes) {
var alt = (0, _jsxAstUtils.getProp)(attributes, 'alt');
if (alt && (0, _jsxAstUtils.getLiteralPropValue)(alt) === '') {
return 'presentation';
}
return 'img';
}

View File

@@ -0,0 +1,195 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _a = require('./a');
var _a2 = _interopRequireDefault(_a);
var _area = require('./area');
var _area2 = _interopRequireDefault(_area);
var _article = require('./article');
var _article2 = _interopRequireDefault(_article);
var _aside = require('./aside');
var _aside2 = _interopRequireDefault(_aside);
var _body = require('./body');
var _body2 = _interopRequireDefault(_body);
var _button = require('./button');
var _button2 = _interopRequireDefault(_button);
var _datalist = require('./datalist');
var _datalist2 = _interopRequireDefault(_datalist);
var _details = require('./details');
var _details2 = _interopRequireDefault(_details);
var _dialog = require('./dialog');
var _dialog2 = _interopRequireDefault(_dialog);
var _dl = require('./dl');
var _dl2 = _interopRequireDefault(_dl);
var _form = require('./form');
var _form2 = _interopRequireDefault(_form);
var _h = require('./h1');
var _h2 = _interopRequireDefault(_h);
var _h3 = require('./h2');
var _h4 = _interopRequireDefault(_h3);
var _h5 = require('./h3');
var _h6 = _interopRequireDefault(_h5);
var _h7 = require('./h4');
var _h8 = _interopRequireDefault(_h7);
var _h9 = require('./h5');
var _h10 = _interopRequireDefault(_h9);
var _h11 = require('./h6');
var _h12 = _interopRequireDefault(_h11);
var _hr = require('./hr');
var _hr2 = _interopRequireDefault(_hr);
var _img = require('./img');
var _img2 = _interopRequireDefault(_img);
var _input = require('./input');
var _input2 = _interopRequireDefault(_input);
var _li = require('./li');
var _li2 = _interopRequireDefault(_li);
var _link = require('./link');
var _link2 = _interopRequireDefault(_link);
var _menu = require('./menu');
var _menu2 = _interopRequireDefault(_menu);
var _menuitem = require('./menuitem');
var _menuitem2 = _interopRequireDefault(_menuitem);
var _meter = require('./meter');
var _meter2 = _interopRequireDefault(_meter);
var _nav = require('./nav');
var _nav2 = _interopRequireDefault(_nav);
var _ol = require('./ol');
var _ol2 = _interopRequireDefault(_ol);
var _option = require('./option');
var _option2 = _interopRequireDefault(_option);
var _output = require('./output');
var _output2 = _interopRequireDefault(_output);
var _progress = require('./progress');
var _progress2 = _interopRequireDefault(_progress);
var _section = require('./section');
var _section2 = _interopRequireDefault(_section);
var _select = require('./select');
var _select2 = _interopRequireDefault(_select);
var _tbody = require('./tbody');
var _tbody2 = _interopRequireDefault(_tbody);
var _textarea = require('./textarea');
var _textarea2 = _interopRequireDefault(_textarea);
var _tfoot = require('./tfoot');
var _tfoot2 = _interopRequireDefault(_tfoot);
var _thead = require('./thead');
var _thead2 = _interopRequireDefault(_thead);
var _ul = require('./ul');
var _ul2 = _interopRequireDefault(_ul);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
exports.default = {
a: _a2.default,
area: _area2.default,
article: _article2.default,
aside: _aside2.default,
body: _body2.default,
button: _button2.default,
datalist: _datalist2.default,
details: _details2.default,
dialog: _dialog2.default,
dl: _dl2.default,
form: _form2.default,
h1: _h2.default,
h2: _h4.default,
h3: _h6.default,
h4: _h8.default,
h5: _h10.default,
h6: _h12.default,
hr: _hr2.default,
img: _img2.default,
input: _input2.default,
li: _li2.default,
link: _link2.default,
menu: _menu2.default,
menuitem: _menuitem2.default,
meter: _meter2.default,
nav: _nav2.default,
ol: _ol2.default,
option: _option2.default,
output: _output2.default,
progress: _progress2.default,
section: _section2.default,
select: _select2.default,
tbody: _tbody2.default,
textarea: _textarea2.default,
tfoot: _tfoot2.default,
thead: _thead2.default,
ul: _ul2.default
};

View File

@@ -0,0 +1,42 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getImplicitRoleForInput;
var _jsxAstUtils = require('jsx-ast-utils');
/**
* Returns the implicit role for an input tag.
*/
function getImplicitRoleForInput(attributes) {
var type = (0, _jsxAstUtils.getProp)(attributes, 'type');
if (type) {
var value = (0, _jsxAstUtils.getLiteralPropValue)(type) || '';
switch (value.toUpperCase()) {
case 'BUTTON':
case 'IMAGE':
case 'RESET':
case 'SUBMIT':
return 'button';
case 'CHECKBOX':
return 'checkbox';
case 'RADIO':
return 'radio';
case 'RANGE':
return 'slider';
case 'EMAIL':
case 'PASSWORD':
case 'SEARCH': // with [list] selector it's combobox
case 'TEL': // with [list] selector it's combobox
case 'URL': // with [list] selector it's combobox
default:
return 'textbox';
}
}
return 'textbox';
}

View File

@@ -0,0 +1,12 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getImplicitRoleForLi;
/**
* Returns the implicit role for an li tag.
*/
function getImplicitRoleForLi() {
return 'listitem';
}

View File

@@ -0,0 +1,19 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getImplicitRoleForLink;
var _jsxAstUtils = require('jsx-ast-utils');
/**
* Returns the implicit role for a link tag.
*/
function getImplicitRoleForLink(attributes) {
if ((0, _jsxAstUtils.getProp)(attributes, 'href')) {
return 'link';
}
return '';
}

View File

@@ -0,0 +1,23 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getImplicitRoleForMenu;
var _jsxAstUtils = require('jsx-ast-utils');
/**
* Returns the implicit role for a menu tag.
*/
function getImplicitRoleForMenu(attributes) {
var type = (0, _jsxAstUtils.getProp)(attributes, 'type');
if (type) {
var value = (0, _jsxAstUtils.getLiteralPropValue)(type);
return value && value.toUpperCase() === 'TOOLBAR' ? 'toolbar' : '';
}
return '';
}

View File

@@ -0,0 +1,32 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getImplicitRoleForMenuitem;
var _jsxAstUtils = require('jsx-ast-utils');
/**
* Returns the implicit role for a menuitem tag.
*/
function getImplicitRoleForMenuitem(attributes) {
var type = (0, _jsxAstUtils.getProp)(attributes, 'type');
if (type) {
var value = (0, _jsxAstUtils.getLiteralPropValue)(type) || '';
switch (value.toUpperCase()) {
case 'COMMAND':
return 'menuitem';
case 'CHECKBOX':
return 'menuitemcheckbox';
case 'RADIO':
return 'menuitemradio';
default:
return '';
}
}
return '';
}

View File

@@ -0,0 +1,12 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getImplicitRoleForMeter;
/**
* Returns the implicit role for a meter tag.
*/
function getImplicitRoleForMeter() {
return 'progressbar';
}

View File

@@ -0,0 +1,12 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getImplicitRoleForNav;
/**
* Returns the implicit role for a nav tag.
*/
function getImplicitRoleForNav() {
return 'navigation';
}

View File

@@ -0,0 +1,12 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getImplicitRoleForOl;
/**
* Returns the implicit role for an ol tag.
*/
function getImplicitRoleForOl() {
return 'list';
}

View File

@@ -0,0 +1,12 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getImplicitRoleForOption;
/**
* Returns the implicit role for an option tag.
*/
function getImplicitRoleForOption() {
return 'option';
}

View File

@@ -0,0 +1,12 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getImplicitRoleForOutput;
/**
* Returns the implicit role for an output tag.
*/
function getImplicitRoleForOutput() {
return 'status';
}

View File

@@ -0,0 +1,12 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getImplicitRoleForProgress;
/**
* Returns the implicit role for a progress tag.
*/
function getImplicitRoleForProgress() {
return 'progressbar';
}

View File

@@ -0,0 +1,12 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getImplicitRoleForSection;
/**
* Returns the implicit role for a section tag.
*/
function getImplicitRoleForSection() {
return 'region';
}

View File

@@ -0,0 +1,12 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getImplicitRoleForSelect;
/**
* Returns the implicit role for a select tag.
*/
function getImplicitRoleForSelect() {
return 'listbox';
}

View File

@@ -0,0 +1,12 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getImplicitRoleForTbody;
/**
* Returns the implicit role for a tbody tag.
*/
function getImplicitRoleForTbody() {
return 'rowgroup';
}

View File

@@ -0,0 +1,12 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getImplicitRoleForTextarea;
/**
* Returns the implicit role for a textarea tag.
*/
function getImplicitRoleForTextarea() {
return 'textbox';
}

View File

@@ -0,0 +1,12 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getImplicitRoleForTfoot;
/**
* Returns the implicit role for a tfoot tag.
*/
function getImplicitRoleForTfoot() {
return 'rowgroup';
}

View File

@@ -0,0 +1,12 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getImplicitRoleForThead;
/**
* Returns the implicit role for a thead tag.
*/
function getImplicitRoleForThead() {
return 'rowgroup';
}

View File

@@ -0,0 +1,12 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = getImplicitRoleForUl;
/**
* Returns the implicit role for a ul tag.
*/
function getImplicitRoleForUl() {
return 'list';
}

View File

@@ -0,0 +1,31 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _ariaQuery = require('aria-query');
var _jsxAstUtils = require('jsx-ast-utils');
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
var abstractRoles = new Set([].concat(_toConsumableArray(_ariaQuery.roles.keys())).filter(function (role) {
return _ariaQuery.roles.get(role).abstract;
}));
var DOMElements = [].concat(_toConsumableArray(_ariaQuery.dom.keys()));
var isAbstractRole = function isAbstractRole(tagName, attributes) {
// Do not test higher level JSX components, as we do not know what
// low-level DOM element this maps to.
if (DOMElements.indexOf(tagName) === -1) {
return false;
}
var role = (0, _jsxAstUtils.getLiteralPropValue)((0, _jsxAstUtils.getProp)(attributes, 'role'));
return abstractRoles.has(role);
};
exports.default = isAbstractRole;

View File

@@ -0,0 +1,29 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _jsxAstUtils = require('jsx-ast-utils');
/**
* Returns boolean indicating that the aria-hidden prop
* is present or the value is true. Will also return true if
* there is an input with type='hidden'.
*
* <div aria-hidden /> is equivalent to the DOM as <div aria-hidden=true />.
*/
var isHiddenFromScreenReader = function isHiddenFromScreenReader(type, attributes) {
if (type.toUpperCase() === 'INPUT') {
var hidden = (0, _jsxAstUtils.getLiteralPropValue)((0, _jsxAstUtils.getProp)(attributes, 'type'));
if (hidden && hidden.toUpperCase() === 'HIDDEN') {
return true;
}
}
var ariaHidden = (0, _jsxAstUtils.getPropValue)((0, _jsxAstUtils.getProp)(attributes, 'aria-hidden'));
return ariaHidden === true;
};
exports.default = isHiddenFromScreenReader;

View File

@@ -0,0 +1,129 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
var _ariaQuery = require('aria-query');
var _axobjectQuery = require('axobject-query');
var _arrayIncludes = require('array-includes');
var _arrayIncludes2 = _interopRequireDefault(_arrayIncludes);
var _attributesComparator = require('./attributesComparator');
var _attributesComparator2 = _interopRequireDefault(_attributesComparator);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
var roleKeys = [].concat(_toConsumableArray(_ariaQuery.roles.keys()));
var elementRoleEntries = [].concat(_toConsumableArray(_ariaQuery.elementRoles));
var nonInteractiveRoles = new Set(roleKeys.filter(function (name) {
var role = _ariaQuery.roles.get(name);
return !role.abstract && !role.superClass.some(function (classes) {
return (0, _arrayIncludes2.default)(classes, 'widget');
});
}));
var interactiveRoles = new Set([].concat(roleKeys,
// 'toolbar' does not descend from widget, but it does support
// aria-activedescendant, thus in practice we treat it as a widget.
'toolbar').filter(function (name) {
var role = _ariaQuery.roles.get(name);
return !role.abstract && role.superClass.some(function (classes) {
return (0, _arrayIncludes2.default)(classes, 'widget');
});
}));
var nonInteractiveElementRoleSchemas = elementRoleEntries.reduce(function (accumulator, _ref) {
var _ref2 = _slicedToArray(_ref, 2),
elementSchema = _ref2[0],
roleSet = _ref2[1];
if ([].concat(_toConsumableArray(roleSet)).every(function (role) {
return nonInteractiveRoles.has(role);
})) {
accumulator.push(elementSchema);
}
return accumulator;
}, []);
var interactiveElementRoleSchemas = elementRoleEntries.reduce(function (accumulator, _ref3) {
var _ref4 = _slicedToArray(_ref3, 2),
elementSchema = _ref4[0],
roleSet = _ref4[1];
if ([].concat(_toConsumableArray(roleSet)).some(function (role) {
return interactiveRoles.has(role);
})) {
accumulator.push(elementSchema);
}
return accumulator;
}, []);
var interactiveAXObjects = new Set([].concat(_toConsumableArray(_axobjectQuery.AXObjects.keys())).filter(function (name) {
return _axobjectQuery.AXObjects.get(name).type === 'widget';
}));
var interactiveElementAXObjectSchemas = [].concat(_toConsumableArray(_axobjectQuery.elementAXObjects)).reduce(function (accumulator, _ref5) {
var _ref6 = _slicedToArray(_ref5, 2),
elementSchema = _ref6[0],
AXObjectSet = _ref6[1];
if ([].concat(_toConsumableArray(AXObjectSet)).every(function (role) {
return interactiveAXObjects.has(role);
})) {
accumulator.push(elementSchema);
}
return accumulator;
}, []);
function checkIsInteractiveElement(tagName, attributes) {
function elementSchemaMatcher(elementSchema) {
return tagName === elementSchema.name && (0, _attributesComparator2.default)(elementSchema.attributes, attributes);
}
// Check in elementRoles for inherent interactive role associations for
// this element.
var isInherentInteractiveElement = interactiveElementRoleSchemas.some(elementSchemaMatcher);
if (isInherentInteractiveElement) {
return true;
}
// Check in elementRoles for inherent non-interactive role associations for
// this element.
var isInherentNonInteractiveElement = nonInteractiveElementRoleSchemas.some(elementSchemaMatcher);
if (isInherentNonInteractiveElement) {
return false;
}
// Check in elementAXObjects for AX Tree associations for this element.
var isInteractiveAXElement = interactiveElementAXObjectSchemas.some(elementSchemaMatcher);
if (isInteractiveAXElement) {
return true;
}
return false;
}
/**
* Returns boolean indicating whether the given element is
* interactive on the DOM or not. Usually used when an element
* has a dynamic handler on it and we need to discern whether or not
* it's intention is to be interacted with on the DOM.
*/
var isInteractiveElement = function isInteractiveElement(tagName, attributes) {
// Do not test higher level JSX components, as we do not know what
// low-level DOM element this maps to.
if (!_ariaQuery.dom.keys(tagName)) {
return false;
}
return checkIsInteractiveElement(tagName, attributes);
};
exports.default = isInteractiveElement;

View File

@@ -0,0 +1,68 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _ariaQuery = require('aria-query');
var _jsxAstUtils = require('jsx-ast-utils');
var _arrayIncludes = require('array-includes');
var _arrayIncludes2 = _interopRequireDefault(_arrayIncludes);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
var roles = [].concat(_toConsumableArray(_ariaQuery.roles.keys()));
var interactiveRoles = roles.filter(function (name) {
return !_ariaQuery.roles.get(name).abstract;
}).filter(function (name) {
return _ariaQuery.roles.get(name).superClass.some(function (klasses) {
return (0, _arrayIncludes2.default)(klasses, 'widget');
});
});
// 'toolbar' does not descend from widget, but it does support
// aria-activedescendant, thus in practice we treat it as a widget.
interactiveRoles.push('toolbar');
/**
* Returns boolean indicating whether the given element has a role
* that is associated with an interactive component. Used when an element
* has a dynamic handler on it and we need to discern whether or not
* its intention is to be interacted with in the DOM.
*
* isInteractiveRole is a Logical Disjunction:
* https://en.wikipedia.org/wiki/Logical_disjunction
* The JSX element does not have a tagName or it has a tagName and a role
* attribute with a value in the set of non-interactive roles.
*/
var isInteractiveRole = function isInteractiveRole(tagName, attributes) {
var value = (0, _jsxAstUtils.getLiteralPropValue)((0, _jsxAstUtils.getProp)(attributes, 'role'));
// If value is undefined, then the role attribute will be dropped in the DOM.
// If value is null, then getLiteralAttributeValue is telling us that the
// value isn't in the form of a literal
if (value === undefined || value === null) {
return false;
}
var isInteractive = false;
var normalizedValues = String(value).toLowerCase().split(' ');
var validRoles = normalizedValues.reduce(function (accumulator, name) {
if ((0, _arrayIncludes2.default)(roles, name)) {
accumulator.push(name);
}
return accumulator;
}, []);
if (validRoles.length > 0) {
// The first role value is a series takes precedence.
isInteractive = (0, _arrayIncludes2.default)(interactiveRoles, validRoles[0]);
}
return isInteractive;
};
exports.default = isInteractiveRole;

View File

@@ -0,0 +1,131 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
var _ariaQuery = require('aria-query');
var _axobjectQuery = require('axobject-query');
var _arrayIncludes = require('array-includes');
var _arrayIncludes2 = _interopRequireDefault(_arrayIncludes);
var _attributesComparator = require('./attributesComparator');
var _attributesComparator2 = _interopRequireDefault(_attributesComparator);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
var roleKeys = [].concat(_toConsumableArray(_ariaQuery.roles.keys()));
var elementRoleEntries = [].concat(_toConsumableArray(_ariaQuery.elementRoles));
var nonInteractiveRoles = new Set(roleKeys.filter(function (name) {
var role = _ariaQuery.roles.get(name);
return !role.abstract && !role.superClass.some(function (classes) {
return (0, _arrayIncludes2.default)(classes, 'widget');
});
}));
var interactiveRoles = new Set([].concat(roleKeys,
// 'toolbar' does not descend from widget, but it does support
// aria-activedescendant, thus in practice we treat it as a widget.
'toolbar').filter(function (name) {
var role = _ariaQuery.roles.get(name);
return !role.abstract && role.superClass.some(function (classes) {
return (0, _arrayIncludes2.default)(classes, 'widget');
});
}));
var nonInteractiveElementRoleSchemas = elementRoleEntries.reduce(function (accumulator, _ref) {
var _ref2 = _slicedToArray(_ref, 2),
elementSchema = _ref2[0],
roleSet = _ref2[1];
if ([].concat(_toConsumableArray(roleSet)).every(function (role) {
return nonInteractiveRoles.has(role);
})) {
accumulator.push(elementSchema);
}
return accumulator;
}, []);
var interactiveElementRoleSchemas = elementRoleEntries.reduce(function (accumulator, _ref3) {
var _ref4 = _slicedToArray(_ref3, 2),
elementSchema = _ref4[0],
roleSet = _ref4[1];
if ([].concat(_toConsumableArray(roleSet)).some(function (role) {
return interactiveRoles.has(role);
})) {
accumulator.push(elementSchema);
}
return accumulator;
}, []);
var nonInteractiveAXObjects = new Set([].concat(_toConsumableArray(_axobjectQuery.AXObjects.keys())).filter(function (name) {
return (0, _arrayIncludes2.default)(['window', 'structure'], _axobjectQuery.AXObjects.get(name).type);
}));
var nonInteractiveElementAXObjectSchemas = [].concat(_toConsumableArray(_axobjectQuery.elementAXObjects)).reduce(function (accumulator, _ref5) {
var _ref6 = _slicedToArray(_ref5, 2),
elementSchema = _ref6[0],
AXObjectSet = _ref6[1];
if ([].concat(_toConsumableArray(AXObjectSet)).every(function (role) {
return nonInteractiveAXObjects.has(role);
})) {
accumulator.push(elementSchema);
}
return accumulator;
}, []);
function checkIsNonInteractiveElement(tagName, attributes) {
function elementSchemaMatcher(elementSchema) {
return tagName === elementSchema.name && (0, _attributesComparator2.default)(elementSchema.attributes, attributes);
}
// Check in elementRoles for inherent non-interactive role associations for
// this element.
var isInherentNonInteractiveElement = nonInteractiveElementRoleSchemas.some(elementSchemaMatcher);
if (isInherentNonInteractiveElement) {
return true;
}
// Check in elementRoles for inherent interactive role associations for
// this element.
var isInherentInteractiveElement = interactiveElementRoleSchemas.some(elementSchemaMatcher);
if (isInherentInteractiveElement) {
return false;
}
// Check in elementAXObjects for AX Tree associations for this element.
var isNonInteractiveAXElement = nonInteractiveElementAXObjectSchemas.some(elementSchemaMatcher);
if (isNonInteractiveAXElement) {
return true;
}
return false;
}
/**
* Returns boolean indicating whether the given element is a non-interactive
* element. If the element has either a non-interactive role assigned or it
* is an element with an inherently non-interactive role, then this utility
* returns true. Elements that lack either an explicitly assigned role or
* an inherent role are not considered. For those, this utility returns false
* because a positive determination of interactiveness cannot be determined.
*/
var isNonInteractiveElement = function isNonInteractiveElement(tagName, attributes) {
// Do not test higher level JSX components, as we do not know what
// low-level DOM element this maps to.
if (!_ariaQuery.dom.has(tagName)) {
return false;
}
return checkIsNonInteractiveElement(tagName, attributes);
};
exports.default = isNonInteractiveElement;

View File

@@ -0,0 +1,71 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _ariaQuery = require('aria-query');
var _jsxAstUtils = require('jsx-ast-utils');
var _arrayIncludes = require('array-includes');
var _arrayIncludes2 = _interopRequireDefault(_arrayIncludes);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
var roles = [].concat(_toConsumableArray(_ariaQuery.roles.keys()));
var nonInteractiveRoles = roles.filter(function (name) {
return !_ariaQuery.roles.get(name).abstract;
}).filter(function (name) {
return !_ariaQuery.roles.get(name).superClass.some(function (klasses) {
return (0, _arrayIncludes2.default)(klasses, 'widget');
});
});
/**
* Returns boolean indicating whether the given element has a role
* that is associated with a non-interactive component. Non-interactive roles
* include `listitem`, `article`, or `dialog`. These are roles that indicate
* for the most part containers.
*
* Elements with these roles should not respond or handle user interactions.
* For example, an `onClick` handler should not be assigned to an element with
* the role `listitem`. An element inside the `listitem`, like a button or a
* link, should handle the click.
*
* This utility returns true for elements that are assigned a non-interactive
* role. It will return false for elements that do not have a role. So whereas
* a `div` might be considered non-interactive, for the purpose of this utility,
* it is considered neither interactive nor non-interactive -- a determination
* cannot be made in this case and false is returned.
*/
var isNonInteractiveRole = function isNonInteractiveRole(tagName, attributes) {
// Do not test higher level JSX components, as we do not know what
// low-level DOM element this maps to.
if (!_ariaQuery.dom.has(tagName)) {
return false;
}
var role = (0, _jsxAstUtils.getLiteralPropValue)((0, _jsxAstUtils.getProp)(attributes, 'role'));
var isNonInteractive = false;
var normalizedValues = String(role).toLowerCase().split(' ');
var validRoles = normalizedValues.reduce(function (accumulator, name) {
if ((0, _arrayIncludes2.default)(roles, name)) {
accumulator.push(name);
}
return accumulator;
}, []);
if (validRoles.length > 0) {
// The first role value is a series takes precedence.
isNonInteractive = (0, _arrayIncludes2.default)(nonInteractiveRoles, validRoles[0]);
}
return isNonInteractive;
};
exports.default = isNonInteractiveRole;

View File

@@ -0,0 +1,15 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
var _jsxAstUtils = require('jsx-ast-utils');
var presentationRoles = new Set(['presentation', 'none']);
var isPresentationRole = function isPresentationRole(tagName, attributes) {
return presentationRoles.has((0, _jsxAstUtils.getLiteralPropValue)((0, _jsxAstUtils.getProp)(attributes, 'role')));
};
exports.default = isPresentationRole;

View File

@@ -0,0 +1,41 @@
'use strict';
Object.defineProperty(exports, "__esModule", {
value: true
});
/**
* JSON schema to accept an array of unique strings
*/
var arraySchema = exports.arraySchema = {
type: 'array',
items: {
type: 'string'
},
uniqueItems: true,
additionalItems: false
};
/**
* JSON schema to accept an array of unique strings from an enumerated list.
*/
var enumArraySchema = exports.enumArraySchema = function enumArraySchema() {
var enumeratedList = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
return Object.assign({}, arraySchema, {
items: {
type: 'string',
enum: enumeratedList
}
});
};
/**
* Factory function to generate an object schema
* with specified properties object
*/
var generateObjSchema = exports.generateObjSchema = function generateObjSchema() {
var properties = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
return {
type: 'object',
properties: properties
};
};