Skip to content

Commit

Permalink
Add rule S6827 (jsx-a11y/anchor-has-content): Anchors should contai…
Browse files Browse the repository at this point in the history
…n accessible content (#4296)
  • Loading branch information
ilia-kebets-sonarsource authored Oct 19, 2023
1 parent 76224c2 commit 9ef0874
Show file tree
Hide file tree
Showing 16 changed files with 232 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"ant-design:components/dropdown/__tests__/index.test.js": [
125
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"ant-design:components/badge/__tests__/index.test.tsx": [
142,
145,
148
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"sonar-web:src/main/js/components/shared/checkbox.js": [
35
]
}
6 changes: 5 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@
"express": "4.18.2",
"functional-red-black-tree": "1.0.1",
"htmlparser2": "9.0.0",
"jsx-ast-utils": "3.3.5",
"lodash.clone": "4.5.0",
"module-alias": "2.2.3",
"postcss-html": "0.36.0",
Expand Down Expand Up @@ -124,6 +125,7 @@
"express",
"functional-red-black-tree",
"htmlparser2",
"jsx-ast-utils",
"lodash.clone",
"module-alias",
"postcss-html",
Expand Down
3 changes: 3 additions & 0 deletions packages/jsts/src/rules/S6827/cb.fixture.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@

<a ref="cancelPending" title={window.t('background_tasks.cancel_all_tasks')} data-toggle="tooltip" href="#"></a>;
<a />; // Noncompliant
28 changes: 28 additions & 0 deletions packages/jsts/src/rules/S6827/cb.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* SonarQube JavaScript Plugin
* Copyright (C) 2011-2023 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { check } from '../tools';
import { rule } from '.';
import path from 'path';

const sonarId = path.basename(__dirname);

describe(`Rule ${sonarId}`, () => {
check(sonarId, rule, __dirname);
});
45 changes: 45 additions & 0 deletions packages/jsts/src/rules/S6827/decorator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* SonarQube JavaScript Plugin
* Copyright (C) 2011-2023 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { Rule } from 'eslint';
import { interceptReport } from '../helpers';
import { hasAnyProp } from 'jsx-ast-utils';
import { TSESTree } from '@typescript-eslint/experimental-utils';

/**
* This fix was introduced in eslint-plugin-jsx-a11y e6bfd5cb7c,
* but the last release is almost a year old, so it doesn't include this.
* When we update the dependency, we can remove this decorator.
*
* This will include the removal of:
* - the `jsx-ast-utils` dependency
* - its type definition `typings/jsx-ast-utils/index.d.ts`
* - all files in the `rules/S6827/` directory
*/
export function decorate(rule: Rule.RuleModule): Rule.RuleModule {
rule.meta!.hasSuggestions = true;
return interceptReport(rule, (context, reportDescriptor) => {
const node = (reportDescriptor as any).node as TSESTree.JSXOpeningElement;

if (hasAnyProp(node.attributes, ['title', 'aria-label'])) {
return;
}
context.report({ ...reportDescriptor });
});
}
25 changes: 25 additions & 0 deletions packages/jsts/src/rules/S6827/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* SonarQube JavaScript Plugin
* Copyright (C) 2011-2023 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
// https://sonarsource.github.io/rspec/#/rspec/S6727/javascript

import { rules as jsxA11yRules } from 'eslint-plugin-jsx-a11y';
import { decorate } from './decorator';

export const rule = decorate(jsxA11yRules['anchor-has-content']);
2 changes: 2 additions & 0 deletions packages/jsts/src/rules/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import { Rule } from 'eslint';

import { rule as S2376 } from './S2376'; // accessor-pairs
import { rule as S6827 } from './S6827'; // anchor-has-content
import { rule as S5850 } from './S5850'; // anchor-precedence
import { rule as S3782 } from './S3782'; // argument-type
import { rule as S2234 } from './S2234'; // arguments-order
Expand Down Expand Up @@ -294,6 +295,7 @@ import { rule as S4817 } from './S4817'; // xpath
const rules: { [key: string]: Rule.RuleModule } = {};

rules['accessor-pairs'] = S2376;
rules['anchor-has-content'] = S6827;
rules['anchor-precedence'] = S5850;
rules['argument-type'] = S3782;
rules['arguments-order'] = S2234;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* SonarQube JavaScript Plugin
* Copyright (C) 2011-2023 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.javascript.checks;

import org.sonar.check.Rule;
import org.sonar.plugins.javascript.api.EslintBasedCheck;
import org.sonar.plugins.javascript.api.JavaScriptRule;
import org.sonar.plugins.javascript.api.TypeScriptRule;

@TypeScriptRule
@JavaScriptRule
@Rule(key = "S6827")
public class AnchorHasContentCheck implements EslintBasedCheck {

@Override
public String eslintKey() {
return "anchor-has-content";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public static List<Class<? extends JavaScriptCheck>> getAllChecks() {
AlertUseCheck.class,
AlphabeticalSortCheck.class,
AlwaysUseCurlyBracesCheck.class,
AnchorHasContentCheck.class,
AnchorPrecedenceCheck.class,
AngleBracketTypeAssertionCheck.class,
ArgumentTypesCheck.class,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<h2>Why is this an issue?</h2>
<p>ARIA (Accessible Rich Internet Applications) is a set of attributes that define ways to make web content and web applications more accessible to
people with disabilities. The 'aria-hidden' attribute is used to indicate that an element and all of its descendants are not visible or perceivable to
any user as implemented by assistive technologies.</p>
<p>However, when 'aria-hidden' is used on a focusable element, it can create a confusing and inaccessible experience for screen reader users. This is
because the element will still be included in the tab order, so a screen reader user can navigate to it, but it will not be announced by the screen
reader due to the 'aria-hidden' attribute.</p>
<p>This rule ensures that focusable elements are not hidden from screen readers using the 'aria-hidden' attribute.</p>
<h2>How to fix it</h2>
<p>Check if the element is focusable. Focusable elements should not have 'aria-hidden' attribute.</p>
<h3>Code examples</h3>
<h4>Noncompliant code example</h4>
<pre data-diff-id="1" data-diff-type="noncompliant">
&lt;button aria-hidden="true"&gt;Click me&lt;/button&gt;
</pre>
<p>Remove 'aria-hidden' attribute.</p>
<h4>Compliant solution</h4>
<pre data-diff-id="1" data-diff-type="compliant">
&lt;button&gt;Click me&lt;/button&gt;
</pre>
<h2>Resources</h2>
<h3>Documentation</h3>
<ul>
<li> <a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques">MDN - Using ARIA: Roles, states, and properties</a>
</li>
<li> <a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-hidden">MDN - aria-hidden attribute (Reference)</a>
</li>
</ul>
<h3>Standards</h3>
<ul>
<li> <a href="https://www.w3.org/TR/wai-aria-1.2/">W3C - Accessible Rich Internet Applications (WAI-ARIA) 1.2</a> </li>
</ul>

Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"title": "Anchors should contain accessible content",
"type": "CODE_SMELL",
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"react",
"accessibility"
],
"defaultSeverity": "Minor",
"ruleSpecification": "RSPEC-6827",
"sqKey": "S6827",
"scope": "All",
"defaultQualityProfiles": ["Sonar way"],
"quickfix": "infeasible",
"code": {
"impacts": {
"MAINTAINABILITY": "LOW",
"RELIABILITY": "LOW"
},
"attribute": "CONVENTIONAL"
},
"compatibleLanguages": [
"JAVASCRIPT",
"TYPESCRIPT"
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,7 @@
"S6822",
"S6823",
"S6824",
"S6825"
"S6825",
"S6827"
]
}
3 changes: 3 additions & 0 deletions typings/jsx-ast-utils/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
declare module 'jsx-ast-utils' {
export const hasAnyProp: (any, any) => boolean;
}

0 comments on commit 9ef0874

Please sign in to comment.