@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,32 @@
|
|
|
1
|
+
# jsx-a11y/tabindex-no-positive
|
|
2
|
+
|
|
3
|
+
💼 This rule is enabled in the following configs: ☑️ `recommended`, 🔒 `strict`.
|
|
4
|
+
|
|
5
|
+
<!-- end auto-generated rule header -->
|
|
6
|
+
|
|
7
|
+
Avoid positive `tabIndex` property values to synchronize the flow of the page with keyboard tab order.
|
|
8
|
+
|
|
9
|
+
## Rule details
|
|
10
|
+
|
|
11
|
+
This rule takes no arguments.
|
|
12
|
+
|
|
13
|
+
### Succeed
|
|
14
|
+
```jsx
|
|
15
|
+
<span tabIndex="0">foo</span>
|
|
16
|
+
<span tabIndex="-1">bar</span>
|
|
17
|
+
<span tabIndex={0}>baz</span>
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### Fail
|
|
21
|
+
```jsx
|
|
22
|
+
<span tabIndex="5">foo</span>
|
|
23
|
+
<span tabIndex="3">bar</span>
|
|
24
|
+
<span tabIndex="1">baz</span>
|
|
25
|
+
<span tabIndex="2">never really sure what goes after baz</span>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Accessibility guidelines
|
|
29
|
+
- [WCAG 2.4.3](https://www.w3.org/WAI/WCAG21/Understanding/focus-order)
|
|
30
|
+
|
|
31
|
+
### Resources
|
|
32
|
+
- [Chrome Audit Rules, AX_FOCUS_03](https://github.com/GoogleChrome/accessibility-developer-tools/wiki/Audit-Rules#ax_focus_03)
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
|
|
4
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
5
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { _defineProperty(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
6
|
+
function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; }
|
|
7
|
+
function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; }
|
|
8
|
+
function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); }
|
|
9
|
+
/* eslint-disable global-require */
|
|
10
|
+
var flatConfigBase = require('./configs/flat-config-base');
|
|
11
|
+
var legacyConfigBase = require('./configs/legacy-config-base');
|
|
12
|
+
var _require = require('../package.json'),
|
|
13
|
+
name = _require.name,
|
|
14
|
+
version = _require.version;
|
|
15
|
+
var allRules = {
|
|
16
|
+
'accessible-emoji': require('./rules/accessible-emoji'),
|
|
17
|
+
'alt-text': require('./rules/alt-text'),
|
|
18
|
+
'anchor-ambiguous-text': require('./rules/anchor-ambiguous-text'),
|
|
19
|
+
'anchor-has-content': require('./rules/anchor-has-content'),
|
|
20
|
+
'anchor-is-valid': require('./rules/anchor-is-valid'),
|
|
21
|
+
'aria-activedescendant-has-tabindex': require('./rules/aria-activedescendant-has-tabindex'),
|
|
22
|
+
'aria-props': require('./rules/aria-props'),
|
|
23
|
+
'aria-proptypes': require('./rules/aria-proptypes'),
|
|
24
|
+
'aria-role': require('./rules/aria-role'),
|
|
25
|
+
'aria-unsupported-elements': require('./rules/aria-unsupported-elements'),
|
|
26
|
+
'autocomplete-valid': require('./rules/autocomplete-valid'),
|
|
27
|
+
'click-events-have-key-events': require('./rules/click-events-have-key-events'),
|
|
28
|
+
'control-has-associated-label': require('./rules/control-has-associated-label'),
|
|
29
|
+
'heading-has-content': require('./rules/heading-has-content'),
|
|
30
|
+
'html-has-lang': require('./rules/html-has-lang'),
|
|
31
|
+
'iframe-has-title': require('./rules/iframe-has-title'),
|
|
32
|
+
'img-redundant-alt': require('./rules/img-redundant-alt'),
|
|
33
|
+
'interactive-supports-focus': require('./rules/interactive-supports-focus'),
|
|
34
|
+
'label-has-associated-control': require('./rules/label-has-associated-control'),
|
|
35
|
+
'label-has-for': require('./rules/label-has-for'),
|
|
36
|
+
lang: require('./rules/lang'),
|
|
37
|
+
'media-has-caption': require('./rules/media-has-caption'),
|
|
38
|
+
'mouse-events-have-key-events': require('./rules/mouse-events-have-key-events'),
|
|
39
|
+
'no-access-key': require('./rules/no-access-key'),
|
|
40
|
+
'no-aria-hidden-on-focusable': require('./rules/no-aria-hidden-on-focusable'),
|
|
41
|
+
'no-autofocus': require('./rules/no-autofocus'),
|
|
42
|
+
'no-distracting-elements': require('./rules/no-distracting-elements'),
|
|
43
|
+
'no-interactive-element-to-noninteractive-role': require('./rules/no-interactive-element-to-noninteractive-role'),
|
|
44
|
+
'no-noninteractive-element-interactions': require('./rules/no-noninteractive-element-interactions'),
|
|
45
|
+
'no-noninteractive-element-to-interactive-role': require('./rules/no-noninteractive-element-to-interactive-role'),
|
|
46
|
+
'no-noninteractive-tabindex': require('./rules/no-noninteractive-tabindex'),
|
|
47
|
+
'no-onchange': require('./rules/no-onchange'),
|
|
48
|
+
'no-redundant-roles': require('./rules/no-redundant-roles'),
|
|
49
|
+
'no-static-element-interactions': require('./rules/no-static-element-interactions'),
|
|
50
|
+
'prefer-tag-over-role': require('./rules/prefer-tag-over-role'),
|
|
51
|
+
'role-has-required-aria-props': require('./rules/role-has-required-aria-props'),
|
|
52
|
+
'role-supports-aria-props': require('./rules/role-supports-aria-props'),
|
|
53
|
+
scope: require('./rules/scope'),
|
|
54
|
+
'tabindex-no-positive': require('./rules/tabindex-no-positive')
|
|
55
|
+
};
|
|
56
|
+
var recommendedRules = {
|
|
57
|
+
'jsx-a11y/alt-text': 'error',
|
|
58
|
+
'jsx-a11y/anchor-ambiguous-text': 'off',
|
|
59
|
+
// TODO: error
|
|
60
|
+
'jsx-a11y/anchor-has-content': 'error',
|
|
61
|
+
'jsx-a11y/anchor-is-valid': 'error',
|
|
62
|
+
'jsx-a11y/aria-activedescendant-has-tabindex': 'error',
|
|
63
|
+
'jsx-a11y/aria-props': 'error',
|
|
64
|
+
'jsx-a11y/aria-proptypes': 'error',
|
|
65
|
+
'jsx-a11y/aria-role': 'error',
|
|
66
|
+
'jsx-a11y/aria-unsupported-elements': 'error',
|
|
67
|
+
'jsx-a11y/autocomplete-valid': 'error',
|
|
68
|
+
'jsx-a11y/click-events-have-key-events': 'error',
|
|
69
|
+
'jsx-a11y/control-has-associated-label': ['off', {
|
|
70
|
+
ignoreElements: ['audio', 'canvas', 'embed', 'input', 'textarea', 'tr', 'video'],
|
|
71
|
+
ignoreRoles: ['grid', 'listbox', 'menu', 'menubar', 'radiogroup', 'row', 'tablist', 'toolbar', 'tree', 'treegrid'],
|
|
72
|
+
includeRoles: ['alert', 'dialog']
|
|
73
|
+
}],
|
|
74
|
+
'jsx-a11y/heading-has-content': 'error',
|
|
75
|
+
'jsx-a11y/html-has-lang': 'error',
|
|
76
|
+
'jsx-a11y/iframe-has-title': 'error',
|
|
77
|
+
'jsx-a11y/img-redundant-alt': 'error',
|
|
78
|
+
'jsx-a11y/interactive-supports-focus': ['error', {
|
|
79
|
+
tabbable: ['button', 'checkbox', 'link', 'searchbox', 'spinbutton', 'switch', 'textbox']
|
|
80
|
+
}],
|
|
81
|
+
'jsx-a11y/label-has-associated-control': 'error',
|
|
82
|
+
'jsx-a11y/label-has-for': 'off',
|
|
83
|
+
'jsx-a11y/media-has-caption': 'error',
|
|
84
|
+
'jsx-a11y/mouse-events-have-key-events': 'error',
|
|
85
|
+
'jsx-a11y/no-access-key': 'error',
|
|
86
|
+
'jsx-a11y/no-autofocus': 'error',
|
|
87
|
+
'jsx-a11y/no-distracting-elements': 'error',
|
|
88
|
+
'jsx-a11y/no-interactive-element-to-noninteractive-role': ['error', {
|
|
89
|
+
tr: ['none', 'presentation'],
|
|
90
|
+
canvas: ['img']
|
|
91
|
+
}],
|
|
92
|
+
'jsx-a11y/no-noninteractive-element-interactions': ['error', {
|
|
93
|
+
handlers: ['onClick', 'onError', 'onLoad', 'onMouseDown', 'onMouseUp', 'onKeyPress', 'onKeyDown', 'onKeyUp'],
|
|
94
|
+
alert: ['onKeyUp', 'onKeyDown', 'onKeyPress'],
|
|
95
|
+
body: ['onError', 'onLoad'],
|
|
96
|
+
dialog: ['onKeyUp', 'onKeyDown', 'onKeyPress'],
|
|
97
|
+
iframe: ['onError', 'onLoad'],
|
|
98
|
+
img: ['onError', 'onLoad']
|
|
99
|
+
}],
|
|
100
|
+
'jsx-a11y/no-noninteractive-element-to-interactive-role': ['error', {
|
|
101
|
+
ul: ['listbox', 'menu', 'menubar', 'radiogroup', 'tablist', 'tree', 'treegrid'],
|
|
102
|
+
ol: ['listbox', 'menu', 'menubar', 'radiogroup', 'tablist', 'tree', 'treegrid'],
|
|
103
|
+
li: ['menuitem', 'menuitemradio', 'menuitemcheckbox', 'option', 'row', 'tab', 'treeitem'],
|
|
104
|
+
table: ['grid'],
|
|
105
|
+
td: ['gridcell'],
|
|
106
|
+
fieldset: ['radiogroup', 'presentation']
|
|
107
|
+
}],
|
|
108
|
+
'jsx-a11y/no-noninteractive-tabindex': ['error', {
|
|
109
|
+
tags: [],
|
|
110
|
+
roles: ['tabpanel'],
|
|
111
|
+
allowExpressionValues: true
|
|
112
|
+
}],
|
|
113
|
+
'jsx-a11y/no-redundant-roles': 'error',
|
|
114
|
+
'jsx-a11y/no-static-element-interactions': ['error', {
|
|
115
|
+
allowExpressionValues: true,
|
|
116
|
+
handlers: ['onClick', 'onMouseDown', 'onMouseUp', 'onKeyPress', 'onKeyDown', 'onKeyUp']
|
|
117
|
+
}],
|
|
118
|
+
'jsx-a11y/role-has-required-aria-props': 'error',
|
|
119
|
+
'jsx-a11y/role-supports-aria-props': 'error',
|
|
120
|
+
'jsx-a11y/scope': 'error',
|
|
121
|
+
'jsx-a11y/tabindex-no-positive': 'error'
|
|
122
|
+
};
|
|
123
|
+
var strictRules = {
|
|
124
|
+
'jsx-a11y/alt-text': 'error',
|
|
125
|
+
'jsx-a11y/anchor-has-content': 'error',
|
|
126
|
+
'jsx-a11y/anchor-is-valid': 'error',
|
|
127
|
+
'jsx-a11y/aria-activedescendant-has-tabindex': 'error',
|
|
128
|
+
'jsx-a11y/aria-props': 'error',
|
|
129
|
+
'jsx-a11y/aria-proptypes': 'error',
|
|
130
|
+
'jsx-a11y/aria-role': 'error',
|
|
131
|
+
'jsx-a11y/aria-unsupported-elements': 'error',
|
|
132
|
+
'jsx-a11y/autocomplete-valid': 'error',
|
|
133
|
+
'jsx-a11y/click-events-have-key-events': 'error',
|
|
134
|
+
'jsx-a11y/control-has-associated-label': ['off', {
|
|
135
|
+
ignoreElements: ['audio', 'canvas', 'embed', 'input', 'textarea', 'tr', 'video'],
|
|
136
|
+
ignoreRoles: ['grid', 'listbox', 'menu', 'menubar', 'radiogroup', 'row', 'tablist', 'toolbar', 'tree', 'treegrid'],
|
|
137
|
+
includeRoles: ['alert', 'dialog']
|
|
138
|
+
}],
|
|
139
|
+
'jsx-a11y/heading-has-content': 'error',
|
|
140
|
+
'jsx-a11y/html-has-lang': 'error',
|
|
141
|
+
'jsx-a11y/iframe-has-title': 'error',
|
|
142
|
+
'jsx-a11y/img-redundant-alt': 'error',
|
|
143
|
+
'jsx-a11y/interactive-supports-focus': ['error', {
|
|
144
|
+
tabbable: ['button', 'checkbox', 'link', 'progressbar', 'searchbox', 'slider', 'spinbutton', 'switch', 'textbox']
|
|
145
|
+
}],
|
|
146
|
+
'jsx-a11y/label-has-for': 'off',
|
|
147
|
+
'jsx-a11y/label-has-associated-control': 'error',
|
|
148
|
+
'jsx-a11y/media-has-caption': 'error',
|
|
149
|
+
'jsx-a11y/mouse-events-have-key-events': 'error',
|
|
150
|
+
'jsx-a11y/no-access-key': 'error',
|
|
151
|
+
'jsx-a11y/no-autofocus': 'error',
|
|
152
|
+
'jsx-a11y/no-distracting-elements': 'error',
|
|
153
|
+
'jsx-a11y/no-interactive-element-to-noninteractive-role': 'error',
|
|
154
|
+
'jsx-a11y/no-noninteractive-element-interactions': ['error', {
|
|
155
|
+
body: ['onError', 'onLoad'],
|
|
156
|
+
iframe: ['onError', 'onLoad'],
|
|
157
|
+
img: ['onError', 'onLoad']
|
|
158
|
+
}],
|
|
159
|
+
'jsx-a11y/no-noninteractive-element-to-interactive-role': 'error',
|
|
160
|
+
'jsx-a11y/no-noninteractive-tabindex': 'error',
|
|
161
|
+
'jsx-a11y/no-redundant-roles': 'error',
|
|
162
|
+
'jsx-a11y/no-static-element-interactions': 'error',
|
|
163
|
+
'jsx-a11y/role-has-required-aria-props': 'error',
|
|
164
|
+
'jsx-a11y/role-supports-aria-props': 'error',
|
|
165
|
+
'jsx-a11y/scope': 'error',
|
|
166
|
+
'jsx-a11y/tabindex-no-positive': 'error'
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
/** Base plugin object */
|
|
170
|
+
var jsxA11y = {
|
|
171
|
+
meta: {
|
|
172
|
+
name,
|
|
173
|
+
version
|
|
174
|
+
},
|
|
175
|
+
rules: _objectSpread({}, allRules)
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Given a ruleset and optionally a flat config name, generate a config.
|
|
180
|
+
* @param {object} rules - ruleset for this config
|
|
181
|
+
* @param {string} flatConfigName - name for the config if flat
|
|
182
|
+
* @returns Config for this set of rules.
|
|
183
|
+
*/
|
|
184
|
+
var createConfig = function createConfig(rules, flatConfigName) {
|
|
185
|
+
return _objectSpread(_objectSpread({}, flatConfigName ? _objectSpread(_objectSpread({}, flatConfigBase), {}, {
|
|
186
|
+
name: "jsx-a11y/".concat(flatConfigName),
|
|
187
|
+
plugins: {
|
|
188
|
+
'jsx-a11y': jsxA11y
|
|
189
|
+
}
|
|
190
|
+
}) : _objectSpread(_objectSpread({}, legacyConfigBase), {}, {
|
|
191
|
+
plugins: ['jsx-a11y']
|
|
192
|
+
})), {}, {
|
|
193
|
+
rules: _objectSpread({}, rules)
|
|
194
|
+
});
|
|
195
|
+
};
|
|
196
|
+
|
|
197
|
+
// Create configs for the plugin object
|
|
198
|
+
var configs = {
|
|
199
|
+
recommended: createConfig(recommendedRules),
|
|
200
|
+
strict: createConfig(strictRules)
|
|
201
|
+
};
|
|
202
|
+
var flatConfigs = {
|
|
203
|
+
recommended: createConfig(recommendedRules, 'recommended'),
|
|
204
|
+
strict: createConfig(strictRules, 'strict')
|
|
205
|
+
};
|
|
206
|
+
module.exports = _objectSpread(_objectSpread({}, jsxA11y), {}, {
|
|
207
|
+
configs,
|
|
208
|
+
flatConfigs
|
|
209
|
+
});
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports["default"] = void 0;
|
|
7
|
+
var _emojiRegex = _interopRequireDefault(require("emoji-regex"));
|
|
8
|
+
var _jsxAstUtils = require("jsx-ast-utils");
|
|
9
|
+
var _safeRegexTest = _interopRequireDefault(require("safe-regex-test"));
|
|
10
|
+
var _schemas = require("../util/schemas");
|
|
11
|
+
var _getElementType = _interopRequireDefault(require("../util/getElementType"));
|
|
12
|
+
var _isHiddenFromScreenReader = _interopRequireDefault(require("../util/isHiddenFromScreenReader"));
|
|
13
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
|
|
14
|
+
/**
|
|
15
|
+
* @fileoverview Enforce emojis are wrapped in <span> and provide screen reader access.
|
|
16
|
+
* @author Ethan Cohen
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
// ----------------------------------------------------------------------------
|
|
20
|
+
// Rule Definition
|
|
21
|
+
// ----------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
var errorMessage = 'Emojis should be wrapped in <span>, have role="img", and have an accessible description with aria-label or aria-labelledby.';
|
|
24
|
+
var schema = (0, _schemas.generateObjSchema)();
|
|
25
|
+
var _default = exports["default"] = {
|
|
26
|
+
meta: {
|
|
27
|
+
docs: {
|
|
28
|
+
description: 'Enforce emojis are wrapped in `<span>` and provide screen reader access.',
|
|
29
|
+
url: 'https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/tree/HEAD/docs/rules/accessible-emoji.md'
|
|
30
|
+
},
|
|
31
|
+
deprecated: true,
|
|
32
|
+
schema: [schema]
|
|
33
|
+
},
|
|
34
|
+
create: function create(context) {
|
|
35
|
+
var elementType = (0, _getElementType["default"])(context);
|
|
36
|
+
var testEmoji = (0, _safeRegexTest["default"])((0, _emojiRegex["default"])());
|
|
37
|
+
return {
|
|
38
|
+
JSXOpeningElement: function JSXOpeningElement(node) {
|
|
39
|
+
var literalChildValue = node.parent.children.find(function (child) {
|
|
40
|
+
return child.type === 'Literal' || child.type === 'JSXText';
|
|
41
|
+
});
|
|
42
|
+
if (literalChildValue && testEmoji(literalChildValue.value)) {
|
|
43
|
+
var elementIsHidden = (0, _isHiddenFromScreenReader["default"])(elementType(node), node.attributes);
|
|
44
|
+
if (elementIsHidden) {
|
|
45
|
+
return; // emoji is decorative
|
|
46
|
+
}
|
|
47
|
+
var rolePropValue = (0, _jsxAstUtils.getLiteralPropValue)((0, _jsxAstUtils.getProp)(node.attributes, 'role'));
|
|
48
|
+
var ariaLabelProp = (0, _jsxAstUtils.getProp)(node.attributes, 'aria-label');
|
|
49
|
+
var arialLabelledByProp = (0, _jsxAstUtils.getProp)(node.attributes, 'aria-labelledby');
|
|
50
|
+
var hasLabel = ariaLabelProp !== undefined || arialLabelledByProp !== undefined;
|
|
51
|
+
var isSpan = elementType(node) === 'span';
|
|
52
|
+
if (hasLabel === false || rolePropValue !== 'img' || isSpan === false) {
|
|
53
|
+
context.report({
|
|
54
|
+
node,
|
|
55
|
+
message: errorMessage
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
module.exports = exports.default;
|
|
@@ -0,0 +1,218 @@
|
|
|
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 _arrayPrototype = _interopRequireDefault(require("array.prototype.flatmap"));
|
|
9
|
+
var _schemas = require("../util/schemas");
|
|
10
|
+
var _getElementType = _interopRequireDefault(require("../util/getElementType"));
|
|
11
|
+
var _hasAccessibleChild = _interopRequireDefault(require("../util/hasAccessibleChild"));
|
|
12
|
+
var _isPresentationRole = _interopRequireDefault(require("../util/isPresentationRole"));
|
|
13
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
|
|
14
|
+
/**
|
|
15
|
+
* @fileoverview Enforce all elements that require alternative text have it.
|
|
16
|
+
* @author Ethan Cohen
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
// ----------------------------------------------------------------------------
|
|
20
|
+
// Rule Definition
|
|
21
|
+
// ----------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
var DEFAULT_ELEMENTS = ['img', 'object', 'area', 'input[type="image"]'];
|
|
24
|
+
var schema = (0, _schemas.generateObjSchema)({
|
|
25
|
+
elements: _schemas.arraySchema,
|
|
26
|
+
img: _schemas.arraySchema,
|
|
27
|
+
object: _schemas.arraySchema,
|
|
28
|
+
area: _schemas.arraySchema,
|
|
29
|
+
'input[type="image"]': _schemas.arraySchema
|
|
30
|
+
});
|
|
31
|
+
var ariaLabelHasValue = function ariaLabelHasValue(prop) {
|
|
32
|
+
var value = (0, _jsxAstUtils.getPropValue)(prop);
|
|
33
|
+
if (value === undefined) {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
if (typeof value === 'string' && value.length === 0) {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
return true;
|
|
40
|
+
};
|
|
41
|
+
var ruleByElement = {
|
|
42
|
+
img(context, node, nodeType) {
|
|
43
|
+
var altProp = (0, _jsxAstUtils.getProp)(node.attributes, 'alt');
|
|
44
|
+
|
|
45
|
+
// Missing alt prop error.
|
|
46
|
+
if (altProp === undefined) {
|
|
47
|
+
if ((0, _isPresentationRole["default"])(nodeType, node.attributes)) {
|
|
48
|
+
context.report({
|
|
49
|
+
node,
|
|
50
|
+
message: 'Prefer alt="" over a presentational role. First rule of aria is to not use aria if it can be achieved via native HTML.'
|
|
51
|
+
});
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
// Check for `aria-label` to provide text alternative
|
|
55
|
+
// Don't create an error if the attribute is used correctly. But if it
|
|
56
|
+
// isn't, suggest that the developer use `alt` instead.
|
|
57
|
+
var ariaLabelProp = (0, _jsxAstUtils.getProp)(node.attributes, 'aria-label');
|
|
58
|
+
if (ariaLabelProp !== undefined) {
|
|
59
|
+
if (!ariaLabelHasValue(ariaLabelProp)) {
|
|
60
|
+
context.report({
|
|
61
|
+
node,
|
|
62
|
+
message: 'The aria-label attribute must have a value. The alt attribute is preferred over aria-label for images.'
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
// Check for `aria-labelledby` to provide text alternative
|
|
68
|
+
// Don't create an error if the attribute is used correctly. But if it
|
|
69
|
+
// isn't, suggest that the developer use `alt` instead.
|
|
70
|
+
var ariaLabelledbyProp = (0, _jsxAstUtils.getProp)(node.attributes, 'aria-labelledby');
|
|
71
|
+
if (ariaLabelledbyProp !== undefined) {
|
|
72
|
+
if (!ariaLabelHasValue(ariaLabelledbyProp)) {
|
|
73
|
+
context.report({
|
|
74
|
+
node,
|
|
75
|
+
message: 'The aria-labelledby attribute must have a value. The alt attribute is preferred over aria-labelledby for images.'
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
context.report({
|
|
81
|
+
node,
|
|
82
|
+
message: "".concat(nodeType, " elements must have an alt prop, either with meaningful text, or an empty string for decorative images.")
|
|
83
|
+
});
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Check if alt prop is undefined.
|
|
88
|
+
var altValue = (0, _jsxAstUtils.getPropValue)(altProp);
|
|
89
|
+
var isNullValued = altProp.value === null; // <img alt />
|
|
90
|
+
|
|
91
|
+
if (altValue && !isNullValued || altValue === '') {
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Undefined alt prop error.
|
|
96
|
+
context.report({
|
|
97
|
+
node,
|
|
98
|
+
message: "Invalid alt value for ".concat(nodeType, ". Use alt=\"\" for presentational images.")
|
|
99
|
+
});
|
|
100
|
+
},
|
|
101
|
+
object(context, node, unusedNodeType, elementType) {
|
|
102
|
+
var ariaLabelProp = (0, _jsxAstUtils.getProp)(node.attributes, 'aria-label');
|
|
103
|
+
var arialLabelledByProp = (0, _jsxAstUtils.getProp)(node.attributes, 'aria-labelledby');
|
|
104
|
+
var hasLabel = ariaLabelHasValue(ariaLabelProp) || ariaLabelHasValue(arialLabelledByProp);
|
|
105
|
+
var titleProp = (0, _jsxAstUtils.getLiteralPropValue)((0, _jsxAstUtils.getProp)(node.attributes, 'title'));
|
|
106
|
+
var hasTitleAttr = !!titleProp;
|
|
107
|
+
if (hasLabel || hasTitleAttr || (0, _hasAccessibleChild["default"])(node.parent, elementType)) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
context.report({
|
|
111
|
+
node,
|
|
112
|
+
message: 'Embedded <object> elements must have alternative text by providing inner text, aria-label or aria-labelledby props.'
|
|
113
|
+
});
|
|
114
|
+
},
|
|
115
|
+
area(context, node) {
|
|
116
|
+
var ariaLabelProp = (0, _jsxAstUtils.getProp)(node.attributes, 'aria-label');
|
|
117
|
+
var arialLabelledByProp = (0, _jsxAstUtils.getProp)(node.attributes, 'aria-labelledby');
|
|
118
|
+
var hasLabel = ariaLabelHasValue(ariaLabelProp) || ariaLabelHasValue(arialLabelledByProp);
|
|
119
|
+
if (hasLabel) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
var altProp = (0, _jsxAstUtils.getProp)(node.attributes, 'alt');
|
|
123
|
+
if (altProp === undefined) {
|
|
124
|
+
context.report({
|
|
125
|
+
node,
|
|
126
|
+
message: 'Each area of an image map must have a text alternative through the `alt`, `aria-label`, or `aria-labelledby` prop.'
|
|
127
|
+
});
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
var altValue = (0, _jsxAstUtils.getPropValue)(altProp);
|
|
131
|
+
var isNullValued = altProp.value === null; // <area alt />
|
|
132
|
+
|
|
133
|
+
if (altValue && !isNullValued || altValue === '') {
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
context.report({
|
|
137
|
+
node,
|
|
138
|
+
message: 'Each area of an image map must have a text alternative through the `alt`, `aria-label`, or `aria-labelledby` prop.'
|
|
139
|
+
});
|
|
140
|
+
},
|
|
141
|
+
'input[type="image"]': function inputImage(context, node, nodeType) {
|
|
142
|
+
// Only test input[type="image"]
|
|
143
|
+
if (nodeType === 'input') {
|
|
144
|
+
var typePropValue = (0, _jsxAstUtils.getPropValue)((0, _jsxAstUtils.getProp)(node.attributes, 'type'));
|
|
145
|
+
if (typePropValue !== 'image') {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
var ariaLabelProp = (0, _jsxAstUtils.getProp)(node.attributes, 'aria-label');
|
|
150
|
+
var arialLabelledByProp = (0, _jsxAstUtils.getProp)(node.attributes, 'aria-labelledby');
|
|
151
|
+
var hasLabel = ariaLabelHasValue(ariaLabelProp) || ariaLabelHasValue(arialLabelledByProp);
|
|
152
|
+
if (hasLabel) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
var altProp = (0, _jsxAstUtils.getProp)(node.attributes, 'alt');
|
|
156
|
+
if (altProp === undefined) {
|
|
157
|
+
context.report({
|
|
158
|
+
node,
|
|
159
|
+
message: '<input> elements with type="image" must have a text alternative through the `alt`, `aria-label`, or `aria-labelledby` prop.'
|
|
160
|
+
});
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
var altValue = (0, _jsxAstUtils.getPropValue)(altProp);
|
|
164
|
+
var isNullValued = altProp.value === null; // <area alt />
|
|
165
|
+
|
|
166
|
+
if (altValue && !isNullValued || altValue === '') {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
context.report({
|
|
170
|
+
node,
|
|
171
|
+
message: '<input> elements with type="image" must have a text alternative through the `alt`, `aria-label`, or `aria-labelledby` prop.'
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
var _default = exports["default"] = {
|
|
176
|
+
meta: {
|
|
177
|
+
docs: {
|
|
178
|
+
url: 'https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/tree/HEAD/docs/rules/alt-text.md',
|
|
179
|
+
description: 'Enforce all elements that require alternative text have meaningful information to relay back to end user.'
|
|
180
|
+
},
|
|
181
|
+
schema: [schema]
|
|
182
|
+
},
|
|
183
|
+
create: function create(context) {
|
|
184
|
+
var options = context.options[0] || {};
|
|
185
|
+
// Elements to validate for alt text.
|
|
186
|
+
var elementOptions = options.elements || DEFAULT_ELEMENTS;
|
|
187
|
+
// Get custom components for just the elements that will be tested.
|
|
188
|
+
var customComponents = (0, _arrayPrototype["default"])(elementOptions, function (element) {
|
|
189
|
+
return options[element];
|
|
190
|
+
});
|
|
191
|
+
var typesToValidate = new Set([].concat(customComponents, elementOptions).map(function (type) {
|
|
192
|
+
return type === 'input[type="image"]' ? 'input' : type;
|
|
193
|
+
}));
|
|
194
|
+
var elementType = (0, _getElementType["default"])(context);
|
|
195
|
+
return {
|
|
196
|
+
JSXOpeningElement(node) {
|
|
197
|
+
var nodeType = elementType(node);
|
|
198
|
+
if (!typesToValidate.has(nodeType)) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
var DOMElement = nodeType;
|
|
202
|
+
if (DOMElement === 'input') {
|
|
203
|
+
DOMElement = 'input[type="image"]';
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Map nodeType to the DOM element if we are running this on a custom component.
|
|
207
|
+
if (elementOptions.indexOf(DOMElement) === -1) {
|
|
208
|
+
DOMElement = elementOptions.find(function (element) {
|
|
209
|
+
var customComponentsForElement = options[element] || [];
|
|
210
|
+
return customComponentsForElement.indexOf(nodeType) > -1;
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
ruleByElement[DOMElement](context, node, nodeType, elementType);
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
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 _schemas = require("../util/schemas");
|
|
8
|
+
var _getAccessibleChildText = _interopRequireDefault(require("../util/getAccessibleChildText"));
|
|
9
|
+
var _getElementType = _interopRequireDefault(require("../util/getElementType"));
|
|
10
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
|
|
11
|
+
/**
|
|
12
|
+
* @fileoverview Enforce anchor text to not exactly match 'click here', 'here', 'link', 'learn more', and user-specified words.
|
|
13
|
+
* @author Matt Wang
|
|
14
|
+
*
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// ----------------------------------------------------------------------------
|
|
18
|
+
// Rule Definition
|
|
19
|
+
// ----------------------------------------------------------------------------
|
|
20
|
+
|
|
21
|
+
var DEFAULT_AMBIGUOUS_WORDS = ['click here', 'here', 'link', 'a link', 'learn more'];
|
|
22
|
+
var schema = (0, _schemas.generateObjSchema)({
|
|
23
|
+
words: _schemas.arraySchema
|
|
24
|
+
});
|
|
25
|
+
var _default = exports["default"] = {
|
|
26
|
+
meta: {
|
|
27
|
+
docs: {
|
|
28
|
+
url: 'https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/tree/HEAD/docs/rules/anchor-ambiguous-text.md',
|
|
29
|
+
description: 'Enforce `<a>` text to not exactly match "click here", "here", "link", or "a link".'
|
|
30
|
+
},
|
|
31
|
+
schema: [schema]
|
|
32
|
+
},
|
|
33
|
+
create: function create(context) {
|
|
34
|
+
var elementType = (0, _getElementType["default"])(context);
|
|
35
|
+
var typesToValidate = ['a'];
|
|
36
|
+
var options = context.options[0] || {};
|
|
37
|
+
var _options$words = options.words,
|
|
38
|
+
words = _options$words === void 0 ? DEFAULT_AMBIGUOUS_WORDS : _options$words;
|
|
39
|
+
var ambiguousWords = new Set(words);
|
|
40
|
+
return {
|
|
41
|
+
JSXOpeningElement: function JSXOpeningElement(node) {
|
|
42
|
+
var nodeType = elementType(node);
|
|
43
|
+
|
|
44
|
+
// Only check anchor elements and custom types.
|
|
45
|
+
if (typesToValidate.indexOf(nodeType) === -1) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
var nodeText = (0, _getAccessibleChildText["default"])(node.parent, elementType);
|
|
49
|
+
if (!ambiguousWords.has(nodeText)) {
|
|
50
|
+
// check the value
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
context.report({
|
|
54
|
+
node,
|
|
55
|
+
message: 'Ambiguous text within anchor. Screen reader users rely on link text for context; the words "{{wordsList}}" are ambiguous and do not provide enough context.',
|
|
56
|
+
data: {
|
|
57
|
+
wordsList: words.join('", "')
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
module.exports = exports.default;
|
|
@@ -0,0 +1,60 @@
|
|
|
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 _getElementType = _interopRequireDefault(require("../util/getElementType"));
|
|
9
|
+
var _schemas = require("../util/schemas");
|
|
10
|
+
var _hasAccessibleChild = _interopRequireDefault(require("../util/hasAccessibleChild"));
|
|
11
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; }
|
|
12
|
+
/**
|
|
13
|
+
* @fileoverview Enforce anchor elements to contain accessible content.
|
|
14
|
+
* @author Lisa Ring & Niklas Holmberg
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
// ----------------------------------------------------------------------------
|
|
18
|
+
// Rule Definition
|
|
19
|
+
// ----------------------------------------------------------------------------
|
|
20
|
+
|
|
21
|
+
var errorMessage = 'Anchors must have content and the content must be accessible by a screen reader.';
|
|
22
|
+
var schema = (0, _schemas.generateObjSchema)({
|
|
23
|
+
components: _schemas.arraySchema
|
|
24
|
+
});
|
|
25
|
+
var _default = exports["default"] = {
|
|
26
|
+
meta: {
|
|
27
|
+
docs: {
|
|
28
|
+
url: 'https://github.com/jsx-eslint/eslint-plugin-jsx-a11y/tree/HEAD/docs/rules/anchor-has-content.md',
|
|
29
|
+
description: 'Enforce all anchors to contain accessible content.'
|
|
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 options = context.options[0] || {};
|
|
38
|
+
var componentOptions = options.components || [];
|
|
39
|
+
var typeCheck = ['a'].concat(componentOptions);
|
|
40
|
+
var nodeType = elementType(node);
|
|
41
|
+
|
|
42
|
+
// Only check anchor elements and custom types.
|
|
43
|
+
if (typeCheck.indexOf(nodeType) === -1) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
if ((0, _hasAccessibleChild["default"])(node.parent, elementType)) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if ((0, _jsxAstUtils.hasAnyProp)(node.attributes, ['title', 'aria-label'])) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
context.report({
|
|
53
|
+
node,
|
|
54
|
+
message: errorMessage
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
module.exports = exports.default;
|