]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-18052 Improve rule details page accessibility
author7PH <benjamin.raymond@sonarsource.com>
Fri, 3 Feb 2023 10:39:25 +0000 (11:39 +0100)
committersonartech <sonartech@sonarsource.com>
Mon, 6 Feb 2023 20:02:53 +0000 (20:02 +0000)
server/sonar-web/src/main/js/app/theme.js
server/sonar-web/src/main/js/apps/coding-rules/__tests__/CodingRules-it.ts
server/sonar-web/src/main/js/apps/coding-rules/components/App.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsMeta.tsx
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/App-test.tsx.snap
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetailsMeta-test.tsx.snap
server/sonar-web/src/main/js/apps/coding-rules/styles.css
server/sonar-web/src/main/js/components/controls/buttons.css
server/sonar-web/src/main/js/components/facet/FacetHeader.tsx
server/sonar-web/src/main/js/components/search-navigator.css
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index 5167a9c05d797e323b860e48e114602b43f62daf..e0be09f66de5d7f84b8b5b046f473201e3a4a324 100644 (file)
@@ -204,6 +204,7 @@ module.exports = {
     smallFontSize: '12px',
     mediumFontSize: '14px',
     bigFontSize: '16px',
+    veryBigFontSize: '18px',
     hugeFontSize: '24px',
     giganticFontSize: '36px',
 
index 0827c6109bfcc57798cd69ebc25997341e0dbe11..900194d53cda04cb1b2bc73ef5c94a77b8688ae5 100644 (file)
@@ -52,7 +52,7 @@ it('should select rules with keyboard navigation', async () => {
   listitem = await screen.findByRole('listitem', { current: true });
   expect(within(listitem).getByRole('link', { name: 'Awsome java rule' })).toBeInTheDocument();
   await user.keyboard('{ArrowRight}');
-  expect(screen.getByRole('heading', { level: 3, name: 'Awsome java rule' })).toBeInTheDocument();
+  expect(screen.getByRole('heading', { level: 1, name: 'Awsome java rule' })).toBeInTheDocument();
   await user.keyboard('{ArrowLeft}');
   listitem = await screen.findByRole('listitem', { current: true });
   expect(within(listitem).getByRole('link', { name: 'Awsome java rule' })).toBeInTheDocument();
@@ -67,7 +67,7 @@ it('should open with permalink', async () => {
 it('should show open rule with default description section', async () => {
   renderCodingRulesApp(undefined, 'coding_rules?open=rule1');
   expect(
-    await screen.findByRole('heading', { level: 3, name: 'Awsome java rule' })
+    await screen.findByRole('heading', { level: 1, name: 'Awsome java rule' })
   ).toBeInTheDocument();
   expect(document.title).toEqual('coding_rule.page.Java.Awsome java rule');
   expect(screen.getByText('Why')).toBeInTheDocument();
@@ -77,7 +77,7 @@ it('should show open rule with default description section', async () => {
 it('should show open rule with no description', async () => {
   renderCodingRulesApp(undefined, 'coding_rules?open=rule6');
   expect(
-    await screen.findByRole('heading', { level: 3, name: 'Bad Python rule' })
+    await screen.findByRole('heading', { level: 1, name: 'Bad Python rule' })
   ).toBeInTheDocument();
   expect(screen.getByText('issue.external_issue_description.Bad Python rule')).toBeInTheDocument();
 });
@@ -85,7 +85,7 @@ it('should show open rule with no description', async () => {
 it('should show hotspot rule section', async () => {
   const user = userEvent.setup();
   renderCodingRulesApp(undefined, 'coding_rules?open=rule2');
-  expect(await screen.findByRole('heading', { level: 3, name: 'Hot hotspot' })).toBeInTheDocument();
+  expect(await screen.findByRole('heading', { level: 1, name: 'Hot hotspot' })).toBeInTheDocument();
   expect(screen.getByText('Introduction to this rule')).toBeInTheDocument();
   expect(
     screen.getByRole('tab', {
@@ -115,7 +115,7 @@ it('should show rule advanced section', async () => {
   const user = userEvent.setup();
   renderCodingRulesApp(undefined, 'coding_rules?open=rule5');
   expect(
-    await screen.findByRole('heading', { level: 3, name: 'Awsome Python rule' })
+    await screen.findByRole('heading', { level: 1, name: 'Awsome Python rule' })
   ).toBeInTheDocument();
   expect(screen.getByText('Introduction to this rule')).toBeInTheDocument();
   expect(
@@ -141,7 +141,7 @@ it('should show rule advanced section with context', async () => {
   const user = userEvent.setup();
   renderCodingRulesApp(undefined, 'coding_rules?open=rule7');
   expect(
-    await screen.findByRole('heading', { level: 3, name: 'Python rule with context' })
+    await screen.findByRole('heading', { level: 1, name: 'Python rule with context' })
   ).toBeInTheDocument();
   expect(
     screen.getByRole('tab', {
@@ -189,7 +189,7 @@ it('should be able to extend the rule description', async () => {
   handler.setIsAdmin();
   renderCodingRulesApp(undefined, 'coding_rules?open=rule5');
   expect(
-    await screen.findByRole('heading', { level: 3, name: 'Awsome Python rule' })
+    await screen.findByRole('heading', { level: 1, name: 'Awsome Python rule' })
   ).toBeInTheDocument();
 
   // Add
@@ -381,7 +381,7 @@ it('should show notification for rule advanced section and remove it after user
   const user = userEvent.setup();
   renderCodingRulesApp(mockLoggedInUser(), 'coding_rules?open=rule8');
   await screen.findByRole('heading', {
-    level: 3,
+    level: 1,
     name: 'Awesome Python rule with education principles',
   });
   expect(
@@ -426,7 +426,7 @@ it('should show notification for rule advanced section and removes it when user
   renderCodingRulesApp(mockLoggedInUser(), 'coding_rules?open=rule8');
 
   await screen.findByRole('heading', {
-    level: 3,
+    level: 1,
     name: 'Awesome Python rule with education principles',
   });
   expect(
index db284a398d9ffb62ab9b8535c35f3ca4ae479760..c69234ee29c9beb3dab4f7370b1085255074007a 100644 (file)
@@ -575,7 +575,11 @@ export class App extends React.PureComponent<Props, State> {
         <div className="layout-page" id="coding-rules-page">
           <ScreenPositionHelper className="layout-page-side-outer">
             {({ top }) => (
-              <div className="layout-page-side" style={{ top }}>
+              <section
+                aria-label={translate('filters')}
+                className="layout-page-side"
+                style={{ top }}
+              >
                 <div className="layout-page-side-inner">
                   <div className="layout-page-filters">
                     <A11ySkipTarget
@@ -605,7 +609,7 @@ export class App extends React.PureComponent<Props, State> {
                     />
                   </div>
                 </div>
-              </div>
+              </section>
             )}
           </ScreenPositionHelper>
 
@@ -653,6 +657,7 @@ export class App extends React.PureComponent<Props, State> {
                 />
               ) : (
                 <>
+                  <h2 className="a11y-hidden">{translate('list_of_rules')}</h2>
                   <ul>
                     {rules.map((rule) => (
                       <RuleListItem
index f83c99f057cfc0102ba4a3e5804da097b1b3cf96..6f248fcb6cce16ffcfe9604d1528b7c0768aaa72 100644 (file)
@@ -244,9 +244,7 @@ export default class RuleDetailsMeta extends React.PureComponent<Props> {
               <SimilarRulesFilter onFilterChange={this.props.onFilterChange} rule={ruleDetails} />
             )}
           </div>
-          <h3 className="page-title coding-rules-detail-header">
-            <big>{ruleDetails.name}</big>
-          </h3>
+          <h1 className="page-title coding-rules-detail-header">{ruleDetails.name}</h1>
         </div>
 
         {hasTypeData && (
index 58ab4ac91cd9bcae2e2dc6aad0078d8a6ab00d7e..08da9918375fc95b9e77150fa2501b21c75a1036 100644 (file)
@@ -101,7 +101,8 @@ exports[`renderBulkButton should show bulk change button when user has global ad
 `;
 
 exports[`should render correctly: loaded (ScreenPositionHelper) 1`] = `
-<div
+<section
+  aria-label="filters"
   className="layout-page-side"
   style={
     {
@@ -177,7 +178,7 @@ exports[`should render correctly: loaded (ScreenPositionHelper) 1`] = `
       />
     </div>
   </div>
-</div>
+</section>
 `;
 
 exports[`should render correctly: loaded 1`] = `
@@ -267,6 +268,11 @@ exports[`should render correctly: loaded 1`] = `
       <div
         className="layout-page-main-inner"
       >
+        <h2
+          className="a11y-hidden"
+        >
+          list_of_rules
+        </h2>
         <ul>
           <RuleListItem
             isLoggedIn={true}
@@ -387,6 +393,11 @@ exports[`should render correctly: loading 1`] = `
       <div
         className="layout-page-main-inner"
       >
+        <h2
+          className="a11y-hidden"
+        >
+          list_of_rules
+        </h2>
         <ul />
       </div>
     </main>
index 9deaf6ffae98176478b119f66d6cba9fcc04e73b..f2e2b7c283cc5f927d7e3b249564cdf3df0e290e 100644 (file)
@@ -46,13 +46,11 @@ exports[`should display right meta info 1`] = `
         }
       />
     </div>
-    <h3
+    <h1
       className="page-title coding-rules-detail-header"
     >
-      <big>
-        Deprecated code should be removed
-      </big>
-    </h3>
+      Deprecated code should be removed
+    </h1>
   </div>
   <ul
     className="coding-rules-detail-properties"
@@ -145,13 +143,11 @@ exports[`should display right meta info 2`] = `
         xoo:OneExternalIssuePerLine
       </span>
     </div>
-    <h3
+    <h1
       className="page-title coding-rules-detail-header"
     >
-      <big>
-        xoo:OneExternalIssuePerLine
-      </big>
-    </h3>
+      xoo:OneExternalIssuePerLine
+    </h1>
   </div>
 </div>
 `;
@@ -172,13 +168,11 @@ exports[`should display right meta info 3`] = `
         xoo:OneExternalIssueWithDetailsPerLine
       </span>
     </div>
-    <h3
+    <h1
       className="page-title coding-rules-detail-header"
     >
-      <big>
-        One external issue per line
-      </big>
-    </h3>
+      One external issue per line
+    </h1>
   </div>
   <ul
     className="coding-rules-detail-properties"
index f7cab25e4cb5affe6aaa67ea29c78b777c8dacec..dc1c9817cf620aad7d41eec52d30723803f41d89 100644 (file)
   margin-top: 0;
 }
 
+.coding-rules-detail-header {
+  font-size: var(--veryBigFontSize);
+}
+
 .coding-rules-detail-properties {
   display: flex;
   flex-wrap: wrap;
index 3f85415cb9d5902c8f81eec1b17c522e43f588cf..1dd0a26769ac0386a994598d398e0b794a90467d 100644 (file)
 }
 
 .button-link.disabled {
-  color: var(--secondFontColor);
+  color: var(--secondFontColor) !important;
   background: transparent !important;
   cursor: default;
 }
index 06319880b8ff7f048eaa9e86fab5422d001132b9..ab17be11ee5aa1f8ce80d7584f7c29a6ef71fdff 100644 (file)
@@ -17,6 +17,7 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
+import classNames from 'classnames';
 import * as React from 'react';
 import { Button, ButtonLink } from '../../components/controls/buttons';
 import HelpTooltip from '../../components/controls/HelpTooltip';
@@ -85,7 +86,11 @@ export default class FacetHeader extends React.PureComponent<Props> {
       name
     );
     return (
-      <div className="search-navigator-facet-header-wrapper display-flex-center">
+      <div
+        className={classNames('search-navigator-facet-header-wrapper display-flex-center', {
+          'expandable-header': this.props.onClick,
+        })}
+      >
         {this.props.onClick ? (
           <span className="search-navigator-facet-header display-flex-center">
             <button
index e7ea7d675df32760dd0f502d9e6c02b8fa29c932..d48be1248f72ee2e36094d82277be1c0ebd113d9 100644 (file)
@@ -257,6 +257,10 @@ button.search-navigator-facet:focus,
   align-items: center;
 }
 
+.search-navigator-facet-header-wrapper:not(.expandable-header) .search-navigator-facet-header {
+  padding-left: 21px;
+}
+
 .search-navigator-facet-list {
   padding-bottom: var(--gridSize);
   font-size: 0;
index 2f89fb5dc42c3d86ff5240ac9c4fc07582707e2e..d3ac510f0070d0df468b8741355eef79840b8dd7 100644 (file)
@@ -110,6 +110,7 @@ line_number=Line Number
 links=Links
 list_of_issues=List of issues
 list_of_projects=List of projects
+list_of_rules=List of rules
 load_more=Load more
 load_verb=Load
 loading=Loading