@arcaauth/eslint-plugin-jsx-a11y 6.10.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.babelrc +17 -0
- package/.eslintrc +44 -0
- package/CHANGELOG.md +774 -0
- package/LICENSE.md +8 -0
- package/README.md +423 -0
- package/__mocks__/IdentifierMock.js +15 -0
- package/__mocks__/JSXAttributeMock.js +39 -0
- package/__mocks__/JSXElementMock.js +37 -0
- package/__mocks__/JSXExpressionContainerMock.js +15 -0
- package/__mocks__/JSXSpreadAttributeMock.js +18 -0
- package/__mocks__/JSXTextMock.js +17 -0
- package/__mocks__/LiteralMock.js +17 -0
- package/__mocks__/genInteractives.js +218 -0
- package/__tests__/__util__/axeMapping.js +6 -0
- package/__tests__/__util__/helpers/getESLintCoreRule.js +9 -0
- package/__tests__/__util__/helpers/parsers.js +186 -0
- package/__tests__/__util__/parserOptionsMapper.js +53 -0
- package/__tests__/__util__/ruleOptionsMapperFactory.js +33 -0
- package/__tests__/index-test.js +40 -0
- package/__tests__/src/rules/accessible-emoji-test.js +66 -0
- package/__tests__/src/rules/alt-text-test.js +291 -0
- package/__tests__/src/rules/anchor-ambiguous-text-test.js +117 -0
- package/__tests__/src/rules/anchor-has-content-test.js +54 -0
- package/__tests__/src/rules/anchor-is-valid-test.js +532 -0
- package/__tests__/src/rules/aria-activedescendant-has-tabindex-test.js +95 -0
- package/__tests__/src/rules/aria-props-test.js +69 -0
- package/__tests__/src/rules/aria-proptypes-test.js +311 -0
- package/__tests__/src/rules/aria-role-test.js +118 -0
- package/__tests__/src/rules/aria-unsupported-elements-test.js +75 -0
- package/__tests__/src/rules/autocomplete-valid-test.js +77 -0
- package/__tests__/src/rules/click-events-have-key-events-test.js +77 -0
- package/__tests__/src/rules/control-has-associated-label-test.js +327 -0
- package/__tests__/src/rules/heading-has-content-test.js +85 -0
- package/__tests__/src/rules/html-has-lang-test.js +42 -0
- package/__tests__/src/rules/iframe-has-title-test.js +55 -0
- package/__tests__/src/rules/img-redundant-alt-test.js +137 -0
- package/__tests__/src/rules/interactive-supports-focus-test.js +267 -0
- package/__tests__/src/rules/label-has-associated-control-test.js +243 -0
- package/__tests__/src/rules/label-has-for-test.js +235 -0
- package/__tests__/src/rules/lang-test.js +59 -0
- package/__tests__/src/rules/media-has-caption-test.js +220 -0
- package/__tests__/src/rules/mouse-events-have-key-events-test.js +154 -0
- package/__tests__/src/rules/no-access-key-test.js +48 -0
- package/__tests__/src/rules/no-aria-hidden-on-focusable-test.js +44 -0
- package/__tests__/src/rules/no-autofocus-test.js +68 -0
- package/__tests__/src/rules/no-distracting-elements-test.js +51 -0
- package/__tests__/src/rules/no-interactive-element-to-noninteractive-role-test.js +405 -0
- package/__tests__/src/rules/no-noninteractive-element-interactions-test.js +502 -0
- package/__tests__/src/rules/no-noninteractive-element-to-interactive-role-test.js +500 -0
- package/__tests__/src/rules/no-noninteractive-tabindex-test.js +123 -0
- package/__tests__/src/rules/no-onchange-test.js +57 -0
- package/__tests__/src/rules/no-redundant-roles-test.js +98 -0
- package/__tests__/src/rules/no-static-element-interactions-test.js +501 -0
- package/__tests__/src/rules/prefer-tag-over-role-test.js +63 -0
- package/__tests__/src/rules/role-has-required-aria-props-test.js +134 -0
- package/__tests__/src/rules/role-supports-aria-props-test.js +570 -0
- package/__tests__/src/rules/scope-test.js +50 -0
- package/__tests__/src/rules/tabindex-no-positive-test.js +55 -0
- package/__tests__/src/util/attributesComparator-test.js +91 -0
- package/__tests__/src/util/getAccessibleChildText-test.js +174 -0
- package/__tests__/src/util/getComputedRole-test.js +71 -0
- package/__tests__/src/util/getElementType-test.js +154 -0
- package/__tests__/src/util/getExplicitRole-test.js +35 -0
- package/__tests__/src/util/getImplicitRole-test.js +25 -0
- package/__tests__/src/util/getSuggestion-test.js +33 -0
- package/__tests__/src/util/getTabIndex-test.js +85 -0
- package/__tests__/src/util/hasAccessibleChild-test.js +157 -0
- package/__tests__/src/util/implicitRoles/input-test.js +87 -0
- package/__tests__/src/util/implicitRoles/menu-test.js +20 -0
- package/__tests__/src/util/implicitRoles/menuitem-test.js +38 -0
- package/__tests__/src/util/isAbstractRole-test.js +51 -0
- package/__tests__/src/util/isContentEditable-test.js +52 -0
- package/__tests__/src/util/isDOMElement-test.js +30 -0
- package/__tests__/src/util/isDisabledElement-test.js +88 -0
- package/__tests__/src/util/isFocusable-test.js +111 -0
- package/__tests__/src/util/isInteractiveElement-test.js +104 -0
- package/__tests__/src/util/isInteractiveRole-test.js +59 -0
- package/__tests__/src/util/isNonInteractiveElement-test.js +97 -0
- package/__tests__/src/util/isNonInteractiveRole-test.js +59 -0
- package/__tests__/src/util/isNonLiteralProperty-test.js +52 -0
- package/__tests__/src/util/isSemanticRoleElement-test.js +72 -0
- package/__tests__/src/util/mayContainChildComponent-test.js +219 -0
- package/__tests__/src/util/mayHaveAccessibleLabel-test.js +256 -0
- package/__tests__/src/util/parserOptionsMapper-test.js +93 -0
- package/__tests__/src/util/schemas-test.js +35 -0
- package/docs/rules/accessible-emoji.md +30 -0
- package/docs/rules/alt-text.md +168 -0
- package/docs/rules/anchor-ambiguous-text.md +91 -0
- package/docs/rules/anchor-has-content.md +64 -0
- package/docs/rules/anchor-is-valid.md +270 -0
- package/docs/rules/aria-activedescendant-has-tabindex.md +52 -0
- package/docs/rules/aria-props.md +29 -0
- package/docs/rules/aria-proptypes.md +30 -0
- package/docs/rules/aria-role.md +51 -0
- package/docs/rules/aria-unsupported-elements.md +30 -0
- package/docs/rules/autocomplete-valid.md +49 -0
- package/docs/rules/click-events-have-key-events.md +28 -0
- package/docs/rules/control-has-associated-label.md +113 -0
- package/docs/rules/heading-has-content.md +67 -0
- package/docs/rules/html-has-lang.md +31 -0
- package/docs/rules/iframe-has-title.md +37 -0
- package/docs/rules/img-redundant-alt.md +48 -0
- package/docs/rules/interactive-supports-focus.md +156 -0
- package/docs/rules/label-has-associated-control.md +152 -0
- package/docs/rules/label-has-for.md +130 -0
- package/docs/rules/lang.md +31 -0
- package/docs/rules/media-has-caption.md +48 -0
- package/docs/rules/mouse-events-have-key-events.md +58 -0
- package/docs/rules/no-access-key.md +30 -0
- package/docs/rules/no-aria-hidden-on-focusable.md +37 -0
- package/docs/rules/no-autofocus.md +43 -0
- package/docs/rules/no-distracting-elements.md +41 -0
- package/docs/rules/no-interactive-element-to-noninteractive-role.md +73 -0
- package/docs/rules/no-noninteractive-element-interactions.md +145 -0
- package/docs/rules/no-noninteractive-element-to-interactive-role.md +76 -0
- package/docs/rules/no-noninteractive-tabindex.md +115 -0
- package/docs/rules/no-onchange.md +36 -0
- package/docs/rules/no-redundant-roles.md +46 -0
- package/docs/rules/no-static-element-interactions.md +114 -0
- package/docs/rules/prefer-tag-over-role.md +32 -0
- package/docs/rules/role-has-required-aria-props.md +31 -0
- package/docs/rules/role-supports-aria-props.md +39 -0
- package/docs/rules/scope.md +30 -0
- package/docs/rules/tabindex-no-positive.md +32 -0
- package/lib/configs/flat-config-base.js +11 -0
- package/lib/configs/legacy-config-base.js +9 -0
- package/lib/index.js +209 -0
- package/lib/rules/accessible-emoji.js +63 -0
- package/lib/rules/alt-text.js +218 -0
- package/lib/rules/anchor-ambiguous-text.js +64 -0
- package/lib/rules/anchor-has-content.js +60 -0
- package/lib/rules/anchor-is-valid.js +122 -0
- package/lib/rules/aria-activedescendant-has-tabindex.js +66 -0
- package/lib/rules/aria-props.js +59 -0
- package/lib/rules/aria-proptypes.js +114 -0
- package/lib/rules/aria-role.js +89 -0
- package/lib/rules/aria-unsupported-elements.js +64 -0
- package/lib/rules/autocomplete-valid.js +67 -0
- package/lib/rules/click-events-have-key-events.js +68 -0
- package/lib/rules/control-has-associated-label.js +103 -0
- package/lib/rules/heading-has-content.js +61 -0
- package/lib/rules/html-has-lang.js +50 -0
- package/lib/rules/iframe-has-title.js +50 -0
- package/lib/rules/img-redundant-alt.js +88 -0
- package/lib/rules/interactive-supports-focus.js +87 -0
- package/lib/rules/label-has-associated-control.js +127 -0
- package/lib/rules/label-has-for.js +150 -0
- package/lib/rules/lang.js +68 -0
- package/lib/rules/media-has-caption.js +96 -0
- package/lib/rules/mouse-events-have-key-events.js +94 -0
- package/lib/rules/no-access-key.js +43 -0
- package/lib/rules/no-aria-hidden-on-focusable.js +47 -0
- package/lib/rules/no-autofocus.js +62 -0
- package/lib/rules/no-distracting-elements.js +54 -0
- package/lib/rules/no-interactive-element-to-noninteractive-role.js +81 -0
- package/lib/rules/no-noninteractive-element-interactions.js +95 -0
- package/lib/rules/no-noninteractive-element-to-interactive-role.js +80 -0
- package/lib/rules/no-noninteractive-tabindex.js +109 -0
- package/lib/rules/no-onchange.js +52 -0
- package/lib/rules/no-redundant-roles.js +86 -0
- package/lib/rules/no-static-element-interactions.js +102 -0
- package/lib/rules/prefer-tag-over-role.js +75 -0
- package/lib/rules/role-has-required-aria-props.js +88 -0
- package/lib/rules/role-supports-aria-props.js +78 -0
- package/lib/rules/scope.js +58 -0
- package/lib/rules/tabindex-no-positive.js +53 -0
- package/lib/util/attributesComparator.js +34 -0
- package/lib/util/getAccessibleChildText.js +55 -0
- package/lib/util/getComputedRole.js +19 -0
- package/lib/util/getElementType.js +30 -0
- package/lib/util/getExplicitRole.js +27 -0
- package/lib/util/getImplicitRole.js +24 -0
- package/lib/util/getSuggestion.js +32 -0
- package/lib/util/getTabIndex.js +34 -0
- package/lib/util/hasAccessibleChild.js +30 -0
- package/lib/util/implicitRoles/a.js +17 -0
- package/lib/util/implicitRoles/area.js +17 -0
- package/lib/util/implicitRoles/article.js +13 -0
- package/lib/util/implicitRoles/aside.js +13 -0
- package/lib/util/implicitRoles/body.js +13 -0
- package/lib/util/implicitRoles/button.js +13 -0
- package/lib/util/implicitRoles/datalist.js +13 -0
- package/lib/util/implicitRoles/details.js +13 -0
- package/lib/util/implicitRoles/dialog.js +13 -0
- package/lib/util/implicitRoles/form.js +13 -0
- package/lib/util/implicitRoles/h1.js +13 -0
- package/lib/util/implicitRoles/h2.js +13 -0
- package/lib/util/implicitRoles/h3.js +13 -0
- package/lib/util/implicitRoles/h4.js +13 -0
- package/lib/util/implicitRoles/h5.js +13 -0
- package/lib/util/implicitRoles/h6.js +13 -0
- package/lib/util/implicitRoles/hr.js +13 -0
- package/lib/util/implicitRoles/img.js +31 -0
- package/lib/util/implicitRoles/index.js +82 -0
- package/lib/util/implicitRoles/input.js +38 -0
- package/lib/util/implicitRoles/li.js +13 -0
- package/lib/util/implicitRoles/link.js +17 -0
- package/lib/util/implicitRoles/menu.js +19 -0
- package/lib/util/implicitRoles/menuitem.js +28 -0
- package/lib/util/implicitRoles/meter.js +13 -0
- package/lib/util/implicitRoles/nav.js +13 -0
- package/lib/util/implicitRoles/ol.js +13 -0
- package/lib/util/implicitRoles/option.js +13 -0
- package/lib/util/implicitRoles/output.js +13 -0
- package/lib/util/implicitRoles/progress.js +13 -0
- package/lib/util/implicitRoles/section.js +13 -0
- package/lib/util/implicitRoles/select.js +13 -0
- package/lib/util/implicitRoles/tbody.js +13 -0
- package/lib/util/implicitRoles/textarea.js +13 -0
- package/lib/util/implicitRoles/tfoot.js +13 -0
- package/lib/util/implicitRoles/thead.js +13 -0
- package/lib/util/implicitRoles/ul.js +13 -0
- package/lib/util/isAbstractRole.js +23 -0
- package/lib/util/isContentEditable.js +13 -0
- package/lib/util/isDOMElement.js +15 -0
- package/lib/util/isDisabledElement.js +23 -0
- package/lib/util/isFocusable.js +23 -0
- package/lib/util/isHiddenFromScreenReader.js +26 -0
- package/lib/util/isInteractiveElement.js +116 -0
- package/lib/util/isInteractiveRole.js +54 -0
- package/lib/util/isNonInteractiveElement.js +131 -0
- package/lib/util/isNonInteractiveRole.js +55 -0
- package/lib/util/isNonLiteralProperty.js +29 -0
- package/lib/util/isPresentationRole.js +13 -0
- package/lib/util/isSemanticRoleElement.js +54 -0
- package/lib/util/mayContainChildComponent.js +50 -0
- package/lib/util/mayHaveAccessibleLabel.js +95 -0
- package/lib/util/schemas.js +52 -0
- package/package.json +120 -0
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports["default"] = void 0;
|
|
7
|
+
var _jsxAstUtils = require("jsx-ast-utils");
|
|
8
|
+
var _safeRegexTest = _interopRequireDefault(require("safe-regex-test"));
|
|
9
|
+
var _schemas = require("../util/schemas");
|
|
10
|
+
var _getElementType = _interopRequireDefault(require("../util/getElementType"));
|
|
11
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
|
|
12
|
+
/**
|
|
13
|
+
* @fileoverview Performs validity check on anchor hrefs. Warns when anchors are used as buttons.
|
|
14
|
+
* @author Almero Steyn
|
|
15
|
+
*
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
// ----------------------------------------------------------------------------
|
|
19
|
+
// Rule Definition
|
|
20
|
+
// ----------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
var allAspects = ['noHref', 'invalidHref', 'preferButton'];
|
|
23
|
+
var preferButtonErrorMessage = 'Anchor used as a button. Anchors are primarily expected to navigate. Use the button element instead. Learn more: https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/HEAD/docs/rules/anchor-is-valid.md';
|
|
24
|
+
var noHrefErrorMessage = 'The href attribute is required for an anchor to be keyboard accessible. Provide a valid, navigable address as the href value. If you cannot provide an href, but still need the element to resemble a link, use a button and change it with appropriate styles. Learn more: https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/HEAD/docs/rules/anchor-is-valid.md';
|
|
25
|
+
var invalidHrefErrorMessage = 'The href attribute requires a valid value to be accessible. Provide a valid, navigable address as the href value. If you cannot provide a valid href, but still need the element to resemble a link, use a button and change it with appropriate styles. Learn more: https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/blob/HEAD/docs/rules/anchor-is-valid.md';
|
|
26
|
+
var schema = (0, _schemas.generateObjSchema)({
|
|
27
|
+
components: _schemas.arraySchema,
|
|
28
|
+
specialLink: _schemas.arraySchema,
|
|
29
|
+
aspects: (0, _schemas.enumArraySchema)(allAspects, 1)
|
|
30
|
+
});
|
|
31
|
+
var _default = exports["default"] = {
|
|
32
|
+
meta: {
|
|
33
|
+
docs: {
|
|
34
|
+
url: 'https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/tree/HEAD/docs/rules/anchor-is-valid.md',
|
|
35
|
+
description: 'Enforce all anchors are valid, navigable elements.'
|
|
36
|
+
},
|
|
37
|
+
schema: [schema]
|
|
38
|
+
},
|
|
39
|
+
create: function create(context) {
|
|
40
|
+
var elementType = (0, _getElementType["default"])(context);
|
|
41
|
+
var testJShref = (0, _safeRegexTest["default"])(/^\W*?javascript:/);
|
|
42
|
+
return {
|
|
43
|
+
JSXOpeningElement: function JSXOpeningElement(node) {
|
|
44
|
+
var attributes = node.attributes;
|
|
45
|
+
var options = context.options[0] || {};
|
|
46
|
+
var componentOptions = options.components || [];
|
|
47
|
+
var typeCheck = ['a'].concat(componentOptions);
|
|
48
|
+
var nodeType = elementType(node);
|
|
49
|
+
|
|
50
|
+
// Only check anchor elements and custom types.
|
|
51
|
+
if (typeCheck.indexOf(nodeType) === -1) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Set up the rule aspects to check.
|
|
56
|
+
var aspects = options.aspects || allAspects;
|
|
57
|
+
|
|
58
|
+
// Create active aspect flag object. Failing checks will only report
|
|
59
|
+
// if the related flag is set to true.
|
|
60
|
+
var activeAspects = {};
|
|
61
|
+
allAspects.forEach(function (aspect) {
|
|
62
|
+
activeAspects[aspect] = aspects.indexOf(aspect) !== -1;
|
|
63
|
+
});
|
|
64
|
+
var propOptions = options.specialLink || [];
|
|
65
|
+
var propsToValidate = ['href'].concat(propOptions);
|
|
66
|
+
var values = propsToValidate.map(function (prop) {
|
|
67
|
+
return (0, _jsxAstUtils.getPropValue)((0, _jsxAstUtils.getProp)(node.attributes, prop));
|
|
68
|
+
});
|
|
69
|
+
// Checks if any actual or custom href prop is provided.
|
|
70
|
+
var hasAnyHref = values.some(function (value) {
|
|
71
|
+
return value != null;
|
|
72
|
+
});
|
|
73
|
+
// Need to check for spread operator as props can be spread onto the element
|
|
74
|
+
// leading to an incorrect validation error.
|
|
75
|
+
var hasSpreadOperator = attributes.some(function (prop) {
|
|
76
|
+
return prop.type === 'JSXSpreadAttribute';
|
|
77
|
+
});
|
|
78
|
+
var onClick = (0, _jsxAstUtils.getProp)(attributes, 'onClick');
|
|
79
|
+
|
|
80
|
+
// When there is no href at all, specific scenarios apply:
|
|
81
|
+
if (!hasAnyHref) {
|
|
82
|
+
// If no spread operator is found and no onClick event is present
|
|
83
|
+
// it is a link without href.
|
|
84
|
+
if (!hasSpreadOperator && activeAspects.noHref && (!onClick || onClick && !activeAspects.preferButton)) {
|
|
85
|
+
context.report({
|
|
86
|
+
node,
|
|
87
|
+
message: noHrefErrorMessage
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
// If no spread operator is found but an onClick is preset it should be a button.
|
|
91
|
+
if (!hasSpreadOperator && onClick && activeAspects.preferButton) {
|
|
92
|
+
context.report({
|
|
93
|
+
node,
|
|
94
|
+
message: preferButtonErrorMessage
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Hrefs have been found, now check for validity.
|
|
101
|
+
var invalidHrefValues = values.filter(function (value) {
|
|
102
|
+
return value != null && typeof value === 'string' && (!value.length || value === '#' || testJShref(value));
|
|
103
|
+
});
|
|
104
|
+
if (invalidHrefValues.length !== 0) {
|
|
105
|
+
// If an onClick is found it should be a button, otherwise it is an invalid link.
|
|
106
|
+
if (onClick && activeAspects.preferButton) {
|
|
107
|
+
context.report({
|
|
108
|
+
node,
|
|
109
|
+
message: preferButtonErrorMessage
|
|
110
|
+
});
|
|
111
|
+
} else if (activeAspects.invalidHref) {
|
|
112
|
+
context.report({
|
|
113
|
+
node,
|
|
114
|
+
message: invalidHrefErrorMessage
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
module.exports = exports.default;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports["default"] = void 0;
|
|
7
|
+
var _ariaQuery = require("aria-query");
|
|
8
|
+
var _jsxAstUtils = require("jsx-ast-utils");
|
|
9
|
+
var _schemas = require("../util/schemas");
|
|
10
|
+
var _getElementType = _interopRequireDefault(require("../util/getElementType"));
|
|
11
|
+
var _getTabIndex = _interopRequireDefault(require("../util/getTabIndex"));
|
|
12
|
+
var _isInteractiveElement = _interopRequireDefault(require("../util/isInteractiveElement"));
|
|
13
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
|
|
14
|
+
/**
|
|
15
|
+
* @fileoverview Enforce elements with aria-activedescendant are tabbable.
|
|
16
|
+
* @author Jesse Beach <@jessebeach>
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
// ----------------------------------------------------------------------------
|
|
20
|
+
// Rule Definition
|
|
21
|
+
// ----------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
var errorMessage = 'An element that manages focus with `aria-activedescendant` must have a tabindex';
|
|
24
|
+
var schema = (0, _schemas.generateObjSchema)();
|
|
25
|
+
var _default = exports["default"] = {
|
|
26
|
+
meta: {
|
|
27
|
+
docs: {
|
|
28
|
+
url: 'https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/tree/HEAD/docs/rules/aria-activedescendant-has-tabindex.md',
|
|
29
|
+
description: 'Enforce elements with aria-activedescendant are tabbable.'
|
|
30
|
+
},
|
|
31
|
+
schema: [schema]
|
|
32
|
+
},
|
|
33
|
+
create: function create(context) {
|
|
34
|
+
var elementType = (0, _getElementType["default"])(context);
|
|
35
|
+
return {
|
|
36
|
+
JSXOpeningElement: function JSXOpeningElement(node) {
|
|
37
|
+
var attributes = node.attributes;
|
|
38
|
+
if ((0, _jsxAstUtils.getProp)(attributes, 'aria-activedescendant') === undefined) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
var type = elementType(node);
|
|
42
|
+
// Do not test higher level JSX components, as we do not know what
|
|
43
|
+
// low-level DOM element this maps to.
|
|
44
|
+
if (!_ariaQuery.dom.has(type)) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
var tabIndex = (0, _getTabIndex["default"])((0, _jsxAstUtils.getProp)(attributes, 'tabIndex'));
|
|
48
|
+
|
|
49
|
+
// If this is an interactive element and the tabindex attribute is not specified,
|
|
50
|
+
// or the tabIndex property was not mutated, then the tabIndex
|
|
51
|
+
// property will be undefined.
|
|
52
|
+
if ((0, _isInteractiveElement["default"])(type, attributes) && tabIndex === undefined) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (tabIndex >= -1) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
context.report({
|
|
59
|
+
node,
|
|
60
|
+
message: errorMessage
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
module.exports = exports.default;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports["default"] = void 0;
|
|
7
|
+
var _ariaQuery = require("aria-query");
|
|
8
|
+
var _jsxAstUtils = require("jsx-ast-utils");
|
|
9
|
+
var _schemas = require("../util/schemas");
|
|
10
|
+
var _getSuggestion = _interopRequireDefault(require("../util/getSuggestion"));
|
|
11
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
|
|
12
|
+
/**
|
|
13
|
+
* @fileoverview Enforce all aria-* properties are valid.
|
|
14
|
+
* @author Ethan Cohen
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// ----------------------------------------------------------------------------
|
|
18
|
+
// Rule Definition
|
|
19
|
+
// ----------------------------------------------------------------------------
|
|
20
|
+
|
|
21
|
+
var ariaAttributes = _ariaQuery.aria.keys();
|
|
22
|
+
var errorMessage = function errorMessage(name) {
|
|
23
|
+
var suggestions = (0, _getSuggestion["default"])(name, ariaAttributes);
|
|
24
|
+
var message = "".concat(name, ": This attribute is an invalid ARIA attribute.");
|
|
25
|
+
if (suggestions.length > 0) {
|
|
26
|
+
return "".concat(message, " Did you mean to use ").concat(suggestions, "?");
|
|
27
|
+
}
|
|
28
|
+
return message;
|
|
29
|
+
};
|
|
30
|
+
var schema = (0, _schemas.generateObjSchema)();
|
|
31
|
+
var _default = exports["default"] = {
|
|
32
|
+
meta: {
|
|
33
|
+
docs: {
|
|
34
|
+
url: 'https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/tree/HEAD/docs/rules/aria-props.md',
|
|
35
|
+
description: 'Enforce all `aria-*` props are valid.'
|
|
36
|
+
},
|
|
37
|
+
schema: [schema]
|
|
38
|
+
},
|
|
39
|
+
create: function create(context) {
|
|
40
|
+
return {
|
|
41
|
+
JSXAttribute: function JSXAttribute(attribute) {
|
|
42
|
+
var name = (0, _jsxAstUtils.propName)(attribute);
|
|
43
|
+
|
|
44
|
+
// `aria` needs to be prefix of property.
|
|
45
|
+
if (name.indexOf('aria-') !== 0) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
var isValid = _ariaQuery.aria.has(name);
|
|
49
|
+
if (isValid === false) {
|
|
50
|
+
context.report({
|
|
51
|
+
node: attribute,
|
|
52
|
+
message: errorMessage(name)
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
module.exports = exports.default;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports["default"] = void 0;
|
|
7
|
+
var _ariaQuery = require("aria-query");
|
|
8
|
+
var _jsxAstUtils = require("jsx-ast-utils");
|
|
9
|
+
var _schemas = require("../util/schemas");
|
|
10
|
+
/**
|
|
11
|
+
* @fileoverview Enforce ARIA state and property values are valid.
|
|
12
|
+
* @author Ethan Cohen
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
// ----------------------------------------------------------------------------
|
|
16
|
+
// Rule Definition
|
|
17
|
+
// ----------------------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
var errorMessage = function errorMessage(name, type, permittedValues) {
|
|
20
|
+
switch (type) {
|
|
21
|
+
case 'tristate':
|
|
22
|
+
return "The value for ".concat(name, " must be a boolean or the string \"mixed\".");
|
|
23
|
+
case 'token':
|
|
24
|
+
return "The value for ".concat(name, " must be a single token from the following: ").concat(permittedValues, ".");
|
|
25
|
+
case 'tokenlist':
|
|
26
|
+
return "The value for ".concat(name, " must be a list of one or more tokens from the following: ").concat(permittedValues, ".");
|
|
27
|
+
case 'idlist':
|
|
28
|
+
return "The value for ".concat(name, " must be a list of strings that represent DOM element IDs (idlist)");
|
|
29
|
+
case 'id':
|
|
30
|
+
return "The value for ".concat(name, " must be a string that represents a DOM element ID");
|
|
31
|
+
case 'boolean':
|
|
32
|
+
case 'string':
|
|
33
|
+
case 'integer':
|
|
34
|
+
case 'number':
|
|
35
|
+
default:
|
|
36
|
+
return "The value for ".concat(name, " must be a ").concat(type, ".");
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
var _validityCheck = function validityCheck(value, expectedType, permittedValues) {
|
|
40
|
+
switch (expectedType) {
|
|
41
|
+
case 'boolean':
|
|
42
|
+
return typeof value === 'boolean';
|
|
43
|
+
case 'string':
|
|
44
|
+
case 'id':
|
|
45
|
+
return typeof value === 'string';
|
|
46
|
+
case 'tristate':
|
|
47
|
+
return typeof value === 'boolean' || value === 'mixed';
|
|
48
|
+
case 'integer':
|
|
49
|
+
case 'number':
|
|
50
|
+
// Booleans resolve to 0/1 values so hard check that it's not first.
|
|
51
|
+
// eslint-disable-next-line no-restricted-globals
|
|
52
|
+
return typeof value !== 'boolean' && isNaN(Number(value)) === false;
|
|
53
|
+
case 'token':
|
|
54
|
+
return permittedValues.indexOf(typeof value === 'string' ? value.toLowerCase() : value) > -1;
|
|
55
|
+
case 'idlist':
|
|
56
|
+
return typeof value === 'string' && value.split(' ').every(function (token) {
|
|
57
|
+
return _validityCheck(token, 'id', []);
|
|
58
|
+
});
|
|
59
|
+
case 'tokenlist':
|
|
60
|
+
return typeof value === 'string' && value.split(' ').every(function (token) {
|
|
61
|
+
return permittedValues.indexOf(token.toLowerCase()) > -1;
|
|
62
|
+
});
|
|
63
|
+
default:
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
var schema = (0, _schemas.generateObjSchema)();
|
|
68
|
+
var _default = exports["default"] = {
|
|
69
|
+
validityCheck: _validityCheck,
|
|
70
|
+
meta: {
|
|
71
|
+
docs: {
|
|
72
|
+
url: 'https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/tree/HEAD/docs/rules/aria-proptypes.md',
|
|
73
|
+
description: 'Enforce ARIA state and property values are valid.'
|
|
74
|
+
},
|
|
75
|
+
schema: [schema]
|
|
76
|
+
},
|
|
77
|
+
create: function create(context) {
|
|
78
|
+
return {
|
|
79
|
+
JSXAttribute: function JSXAttribute(attribute) {
|
|
80
|
+
var name = (0, _jsxAstUtils.propName)(attribute);
|
|
81
|
+
var normalizedName = name.toLowerCase();
|
|
82
|
+
|
|
83
|
+
// Not a valid aria-* state or property.
|
|
84
|
+
if (normalizedName.indexOf('aria-') !== 0 || _ariaQuery.aria.get(normalizedName) === undefined) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Ignore the attribute if its value is null or undefined.
|
|
89
|
+
if ((0, _jsxAstUtils.getPropValue)(attribute) == null) return;
|
|
90
|
+
var value = (0, _jsxAstUtils.getLiteralPropValue)(attribute);
|
|
91
|
+
|
|
92
|
+
// Ignore the attribute if its value is not a literal.
|
|
93
|
+
if (value === null) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// These are the attributes of the property/state to check against.
|
|
98
|
+
var attributes = _ariaQuery.aria.get(normalizedName);
|
|
99
|
+
var permittedType = attributes.type;
|
|
100
|
+
var allowUndefined = attributes.allowUndefined || false;
|
|
101
|
+
var permittedValues = attributes.values || [];
|
|
102
|
+
var isValid = _validityCheck(value, permittedType, permittedValues) || allowUndefined && value === undefined;
|
|
103
|
+
if (isValid) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
context.report({
|
|
107
|
+
node: attribute,
|
|
108
|
+
message: errorMessage(name, permittedType, permittedValues)
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
module.exports = exports.default;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports["default"] = void 0;
|
|
7
|
+
var _ariaQuery = require("aria-query");
|
|
8
|
+
var _jsxAstUtils = require("jsx-ast-utils");
|
|
9
|
+
var _getElementType = _interopRequireDefault(require("../util/getElementType"));
|
|
10
|
+
var _schemas = require("../util/schemas");
|
|
11
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
|
|
12
|
+
/**
|
|
13
|
+
* @fileoverview Enforce aria role attribute is valid.
|
|
14
|
+
* @author Ethan Cohen
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// ----------------------------------------------------------------------------
|
|
18
|
+
// Rule Definition
|
|
19
|
+
// ----------------------------------------------------------------------------
|
|
20
|
+
|
|
21
|
+
var errorMessage = 'Elements with ARIA roles must use a valid, non-abstract ARIA role.';
|
|
22
|
+
var schema = (0, _schemas.generateObjSchema)({
|
|
23
|
+
allowedInvalidRoles: {
|
|
24
|
+
items: {
|
|
25
|
+
type: 'string'
|
|
26
|
+
},
|
|
27
|
+
type: 'array',
|
|
28
|
+
uniqueItems: true
|
|
29
|
+
},
|
|
30
|
+
ignoreNonDOM: {
|
|
31
|
+
type: 'boolean',
|
|
32
|
+
"default": false
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
var validRoles = new Set(_ariaQuery.roles.keys().filter(function (role) {
|
|
36
|
+
return _ariaQuery.roles.get(role)["abstract"] === false;
|
|
37
|
+
}));
|
|
38
|
+
var _default = exports["default"] = {
|
|
39
|
+
meta: {
|
|
40
|
+
docs: {
|
|
41
|
+
url: 'https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/tree/HEAD/docs/rules/aria-role.md',
|
|
42
|
+
description: 'Enforce that elements with ARIA roles must use a valid, non-abstract ARIA role.'
|
|
43
|
+
},
|
|
44
|
+
schema: [schema]
|
|
45
|
+
},
|
|
46
|
+
create: function create(context) {
|
|
47
|
+
var options = context.options[0] || {};
|
|
48
|
+
var ignoreNonDOM = !!options.ignoreNonDOM;
|
|
49
|
+
var allowedInvalidRoles = new Set(options.allowedInvalidRoles || []);
|
|
50
|
+
var elementType = (0, _getElementType["default"])(context);
|
|
51
|
+
return {
|
|
52
|
+
JSXAttribute: function JSXAttribute(attribute) {
|
|
53
|
+
// If ignoreNonDOM and the parent isn't DOM, don't run rule.
|
|
54
|
+
if (ignoreNonDOM) {
|
|
55
|
+
var type = elementType(attribute.parent);
|
|
56
|
+
if (!_ariaQuery.dom.get(type)) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Get prop name
|
|
62
|
+
var name = (0, _jsxAstUtils.propName)(attribute).toUpperCase();
|
|
63
|
+
if (name !== 'ROLE') {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
var value = (0, _jsxAstUtils.getLiteralPropValue)(attribute);
|
|
67
|
+
|
|
68
|
+
// If value is undefined, then the role attribute will be dropped in the DOM.
|
|
69
|
+
// If value is null, then getLiteralAttributeValue is telling us that the
|
|
70
|
+
// value isn't in the form of a literal.
|
|
71
|
+
if (value === undefined || value === null) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
var values = String(value).split(' ');
|
|
75
|
+
var isValid = values.every(function (val) {
|
|
76
|
+
return allowedInvalidRoles.has(val) || validRoles.has(val);
|
|
77
|
+
});
|
|
78
|
+
if (isValid === true) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
context.report({
|
|
82
|
+
node: attribute,
|
|
83
|
+
message: errorMessage
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
module.exports = exports.default;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports["default"] = void 0;
|
|
7
|
+
var _ariaQuery = require("aria-query");
|
|
8
|
+
var _jsxAstUtils = require("jsx-ast-utils");
|
|
9
|
+
var _schemas = require("../util/schemas");
|
|
10
|
+
var _getElementType = _interopRequireDefault(require("../util/getElementType"));
|
|
11
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
|
|
12
|
+
/**
|
|
13
|
+
* @fileoverview Enforce that elements that do not support ARIA roles,
|
|
14
|
+
* states and properties do not have those attributes.
|
|
15
|
+
* @author Ethan Cohen
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
// ----------------------------------------------------------------------------
|
|
19
|
+
// Rule Definition
|
|
20
|
+
// ----------------------------------------------------------------------------
|
|
21
|
+
|
|
22
|
+
var errorMessage = function errorMessage(invalidProp) {
|
|
23
|
+
return "This element does not support ARIA roles, states and properties. Try removing the prop '".concat(invalidProp, "'.");
|
|
24
|
+
};
|
|
25
|
+
var invalidAttributes = new Set(_ariaQuery.aria.keys().concat('role'));
|
|
26
|
+
var schema = (0, _schemas.generateObjSchema)();
|
|
27
|
+
var _default = exports["default"] = {
|
|
28
|
+
meta: {
|
|
29
|
+
docs: {
|
|
30
|
+
url: 'https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/tree/HEAD/docs/rules/aria-unsupported-elements.md',
|
|
31
|
+
description: 'Enforce that elements that do not support ARIA roles, states, and properties do not have those attributes.'
|
|
32
|
+
},
|
|
33
|
+
schema: [schema]
|
|
34
|
+
},
|
|
35
|
+
create: function create(context) {
|
|
36
|
+
var elementType = (0, _getElementType["default"])(context);
|
|
37
|
+
return {
|
|
38
|
+
JSXOpeningElement: function JSXOpeningElement(node) {
|
|
39
|
+
var nodeType = elementType(node);
|
|
40
|
+
var nodeAttrs = _ariaQuery.dom.get(nodeType) || {};
|
|
41
|
+
var _nodeAttrs$reserved = nodeAttrs.reserved,
|
|
42
|
+
isReservedNodeType = _nodeAttrs$reserved === void 0 ? false : _nodeAttrs$reserved;
|
|
43
|
+
|
|
44
|
+
// If it's not reserved, then it can have aria-* roles, states, and properties
|
|
45
|
+
if (isReservedNodeType === false) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
node.attributes.forEach(function (prop) {
|
|
49
|
+
if (prop.type === 'JSXSpreadAttribute') {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
var name = (0, _jsxAstUtils.propName)(prop).toLowerCase();
|
|
53
|
+
if (invalidAttributes.has(name)) {
|
|
54
|
+
context.report({
|
|
55
|
+
node,
|
|
56
|
+
message: errorMessage(name)
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
module.exports = exports.default;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports["default"] = void 0;
|
|
7
|
+
var _axeCore = require("axe-core");
|
|
8
|
+
var _jsxAstUtils = require("jsx-ast-utils");
|
|
9
|
+
var _schemas = require("../util/schemas");
|
|
10
|
+
var _getElementType = _interopRequireDefault(require("../util/getElementType"));
|
|
11
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
|
|
12
|
+
/**
|
|
13
|
+
* @fileoverview Ensure autocomplete attribute is correct.
|
|
14
|
+
* @author Wilco Fiers
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// ----------------------------------------------------------------------------
|
|
18
|
+
// Rule Definition
|
|
19
|
+
// ----------------------------------------------------------------------------
|
|
20
|
+
|
|
21
|
+
var schema = (0, _schemas.generateObjSchema)({
|
|
22
|
+
inputComponents: _schemas.arraySchema
|
|
23
|
+
});
|
|
24
|
+
var _default = exports["default"] = {
|
|
25
|
+
meta: {
|
|
26
|
+
docs: {
|
|
27
|
+
url: 'https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/tree/HEAD/docs/rules/autocomplete-valid.md',
|
|
28
|
+
description: 'Enforce that autocomplete attributes are used correctly.'
|
|
29
|
+
},
|
|
30
|
+
schema: [schema]
|
|
31
|
+
},
|
|
32
|
+
create: function create(context) {
|
|
33
|
+
var elementType = (0, _getElementType["default"])(context);
|
|
34
|
+
return {
|
|
35
|
+
JSXOpeningElement: function JSXOpeningElement(node) {
|
|
36
|
+
var options = context.options[0] || {};
|
|
37
|
+
var _options$inputCompone = options.inputComponents,
|
|
38
|
+
inputComponents = _options$inputCompone === void 0 ? [] : _options$inputCompone;
|
|
39
|
+
var inputTypes = ['input'].concat(inputComponents);
|
|
40
|
+
var elType = elementType(node);
|
|
41
|
+
var autocomplete = (0, _jsxAstUtils.getLiteralPropValue)((0, _jsxAstUtils.getProp)(node.attributes, 'autocomplete'));
|
|
42
|
+
if (typeof autocomplete !== 'string' || !inputTypes.includes(elType)) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
var type = (0, _jsxAstUtils.getLiteralPropValue)((0, _jsxAstUtils.getProp)(node.attributes, 'type'));
|
|
46
|
+
var _runVirtualRule = (0, _axeCore.runVirtualRule)('autocomplete-valid', {
|
|
47
|
+
nodeName: 'input',
|
|
48
|
+
attributes: {
|
|
49
|
+
autocomplete,
|
|
50
|
+
// Which autocomplete is valid depends on the input type
|
|
51
|
+
type: type === null ? undefined : type
|
|
52
|
+
}
|
|
53
|
+
}),
|
|
54
|
+
violations = _runVirtualRule.violations;
|
|
55
|
+
if (violations.length === 0) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
// Since we only test one rule, with one node, return the message from first (and only) instance of each
|
|
59
|
+
context.report({
|
|
60
|
+
node,
|
|
61
|
+
message: violations[0].nodes[0].all[0].message
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
module.exports = exports.default;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports["default"] = void 0;
|
|
7
|
+
var _ariaQuery = require("aria-query");
|
|
8
|
+
var _jsxAstUtils = require("jsx-ast-utils");
|
|
9
|
+
var _schemas = require("../util/schemas");
|
|
10
|
+
var _getElementType = _interopRequireDefault(require("../util/getElementType"));
|
|
11
|
+
var _isHiddenFromScreenReader = _interopRequireDefault(require("../util/isHiddenFromScreenReader"));
|
|
12
|
+
var _isInteractiveElement = _interopRequireDefault(require("../util/isInteractiveElement"));
|
|
13
|
+
var _isPresentationRole = _interopRequireDefault(require("../util/isPresentationRole"));
|
|
14
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
|
|
15
|
+
/**
|
|
16
|
+
* @fileoverview Enforce a clickable non-interactive element has at least 1 keyboard event listener.
|
|
17
|
+
* @author Ethan Cohen
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
// ----------------------------------------------------------------------------
|
|
21
|
+
// Rule Definition
|
|
22
|
+
// ----------------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
var errorMessage = 'Visible, non-interactive elements with click handlers must have at least one keyboard listener.';
|
|
25
|
+
var schema = (0, _schemas.generateObjSchema)();
|
|
26
|
+
var _default = exports["default"] = {
|
|
27
|
+
meta: {
|
|
28
|
+
docs: {
|
|
29
|
+
url: 'https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/tree/HEAD/docs/rules/click-events-have-key-events.md',
|
|
30
|
+
description: 'Enforce a clickable non-interactive element has at least one keyboard event listener.'
|
|
31
|
+
},
|
|
32
|
+
schema: [schema]
|
|
33
|
+
},
|
|
34
|
+
create: function create(context) {
|
|
35
|
+
var elementType = (0, _getElementType["default"])(context);
|
|
36
|
+
return {
|
|
37
|
+
JSXOpeningElement: function JSXOpeningElement(node) {
|
|
38
|
+
var props = node.attributes;
|
|
39
|
+
if ((0, _jsxAstUtils.getProp)(props, 'onclick') === undefined) {
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
var type = elementType(node);
|
|
43
|
+
var requiredProps = ['onkeydown', 'onkeyup', 'onkeypress'];
|
|
44
|
+
if (!_ariaQuery.dom.has(type)) {
|
|
45
|
+
// Do not test higher level JSX components, as we do not know what
|
|
46
|
+
// low-level DOM element this maps to.
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if ((0, _isHiddenFromScreenReader["default"])(type, props) || (0, _isPresentationRole["default"])(type, props)) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if ((0, _isInteractiveElement["default"])(type, props)) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if ((0, _jsxAstUtils.hasAnyProp)(props, requiredProps)) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Visible, non-interactive elements with click handlers require one keyboard event listener.
|
|
60
|
+
context.report({
|
|
61
|
+
node,
|
|
62
|
+
message: errorMessage
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
module.exports = exports.default;
|