]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-11774 Make issues more accessible to screen readers
authorWouter Admiraal <wouter.admiraal@sonarsource.com>
Thu, 25 Feb 2021 14:12:50 +0000 (15:12 +0100)
committersonartech <sonartech@sonarsource.com>
Wed, 3 Mar 2021 20:12:51 +0000 (20:12 +0000)
30 files changed:
server/sonar-web/src/main/js/app/styles/init/lists.css
server/sonar-web/src/main/js/apps/issues/components/ComponentBreadcrumbs.tsx
server/sonar-web/src/main/js/apps/issues/components/IssuesList.tsx
server/sonar-web/src/main/js/apps/issues/components/ListItem.tsx
server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/ComponentBreadcrumbs-test.tsx.snap
server/sonar-web/src/main/js/apps/issues/components/__tests__/__snapshots__/IssuesList-test.tsx.snap
server/sonar-web/src/main/js/components/issue/IssueView.tsx
server/sonar-web/src/main/js/components/issue/__tests__/__snapshots__/IssueView-test.tsx.snap
server/sonar-web/src/main/js/components/issue/components/IssueActionsBar.tsx
server/sonar-web/src/main/js/components/issue/components/IssueAssign.tsx
server/sonar-web/src/main/js/components/issue/components/IssueChangelog.tsx
server/sonar-web/src/main/js/components/issue/components/IssueCommentAction.tsx
server/sonar-web/src/main/js/components/issue/components/IssueCommentLine.tsx
server/sonar-web/src/main/js/components/issue/components/IssueSeverity.tsx
server/sonar-web/src/main/js/components/issue/components/IssueTags.tsx
server/sonar-web/src/main/js/components/issue/components/IssueTitleBar.tsx
server/sonar-web/src/main/js/components/issue/components/IssueTransition.tsx
server/sonar-web/src/main/js/components/issue/components/IssueType.tsx
server/sonar-web/src/main/js/components/issue/components/SimilarIssuesFilter.tsx
server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueActionsBar-test.tsx.snap
server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueAssign-test.tsx.snap
server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueChangelog-test.tsx.snap
server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueCommentAction-test.tsx.snap
server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueCommentLine-test.tsx.snap
server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueSeverity-test.tsx.snap
server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueTags-test.tsx.snap
server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueTitleBar-test.tsx.snap
server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueTransition-test.tsx.snap
server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueType-test.tsx.snap
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index 332846611e8a13929150f446b20d807ea1f7c509..7c59ae9a63e719480efbfff6c643a6cf2d274aca 100644 (file)
@@ -46,7 +46,8 @@ ol.list-styled {
   list-style: none;
 }
 
-.list-inline > li {
+ul.list-inline > li,
+div.list-inline > div {
   display: inline-block;
   vertical-align: top;
   padding-right: 5px;
index cacfe24b82819d7f73955821db92a7e50a14b99b..0de4e072f385696983485f708f78f17e07b139bd 100644 (file)
@@ -19,6 +19,7 @@
  */
 import * as React from 'react';
 import QualifierIcon from 'sonar-ui-common/components/icons/QualifierIcon';
+import { translateWithParameters } from 'sonar-ui-common/helpers/l10n';
 import { collapsePath, limitComponentName } from 'sonar-ui-common/helpers/path';
 import { getSelectedLocation } from '../utils';
 
@@ -42,7 +43,12 @@ export default function ComponentBreadcrumbs({
   const componentName = selectedLocation ? selectedLocation.componentName : issue.componentLongName;
 
   return (
-    <div className="component-name text-ellipsis">
+    <div
+      aria-label={translateWithParameters(
+        'issues.on_file_x',
+        `${displayProject ? issue.projectName + ', ' : ''}${componentName}`
+      )}
+      className="component-name text-ellipsis">
       <QualifierIcon className="spacer-right" qualifier={issue.componentQualifier} />
 
       {displayProject && (
index 3f9c71fea3f0d9a73d8309d62b89bab9679858dd..783876c7ad324136ff1eff27d7298ac521190b02 100644 (file)
@@ -70,7 +70,7 @@ export default class IssuesList extends React.PureComponent<Props, State> {
     }
 
     return (
-      <div>
+      <ul>
         {issues.map((issue, index) => (
           <ListItem
             branchLike={branchLike}
@@ -88,7 +88,7 @@ export default class IssuesList extends React.PureComponent<Props, State> {
             selected={selectedIssue != null && selectedIssue.key === issue.key}
           />
         ))}
-      </div>
+      </ul>
     );
   }
 }
index 8e388992ce01c01b20070d788a08c3d5075de0bb..ec7b2fb4397af8927d63be40e10706a38022750e 100644 (file)
@@ -95,7 +95,7 @@ export default class ListItem extends React.PureComponent<Props> {
     const displayComponent = !previousIssue || previousIssue.component !== issue.component;
 
     return (
-      <div className="issues-workspace-list-item">
+      <li className="issues-workspace-list-item">
         {displayComponent && (
           <div className="issues-workspace-list-component note">
             <ComponentBreadcrumbs component={component} issue={this.props.issue} />
@@ -113,7 +113,7 @@ export default class ListItem extends React.PureComponent<Props> {
           openPopup={this.props.openPopup}
           selected={this.props.selected}
         />
-      </div>
+      </li>
     );
   }
 }
index bc18b11ab3a2b095ee9dedb1b7e1163445c0316b..3ecf7df7f33efcbceede756032e3d2b668fcd80b 100644 (file)
@@ -2,6 +2,7 @@
 
 exports[`renders 1`] = `
 <div
+  aria-label="issues.on_file_x.proj-name, comp-name"
   className="component-name text-ellipsis"
 >
   <QualifierIcon
@@ -26,6 +27,7 @@ exports[`renders 1`] = `
 
 exports[`renders with sub-project 1`] = `
 <div
+  aria-label="issues.on_file_x.proj-name, comp-name"
   className="component-name text-ellipsis"
 >
   <QualifierIcon
index 975eb1fe1cf0352df71ae025241e8ad6f9f1512d..7fed2c7faa092c4164bbd49db6da12e2b2deb90f 100644 (file)
@@ -9,7 +9,7 @@ exports[`should render correctly 1`] = `
 `;
 
 exports[`should render correctly 2`] = `
-<div>
+<ul>
   <ListItem
     checked={false}
     issue={
@@ -123,5 +123,5 @@ exports[`should render correctly 2`] = `
     }
     selected={false}
   />
-</div>
+</ul>
 `;
index da13b30550ab2b2899c5b14184329194ea2c4466..98d1d885a66b228109bb95caf0cb84d43148e303 100644 (file)
@@ -77,12 +77,7 @@ export default class IssueView extends React.PureComponent<Props> {
     });
 
     return (
-      <div
-        className={issueClass}
-        data-issue={issue.key}
-        onClick={this.handleClick}
-        role="listitem"
-        tabIndex={0}>
+      <div className={issueClass} data-issue={issue.key} onClick={this.handleClick} role="group">
         <IssueTitleBar
           branchLike={this.props.branchLike}
           currentPopup={this.props.currentPopup}
index dfa6de16cee36ce8c4569e30a5b2be6c2b7be37a..a8463a07f3d5ae9ace6728c01570081c2e260f89 100644 (file)
@@ -5,8 +5,7 @@ exports[`should render hotspots correctly 1`] = `
   className="issue hotspot selected"
   data-issue="AVsae-CQS-9G3txfbFN2"
   onClick={[Function]}
-  role="listitem"
-  tabIndex={0}
+  role="group"
 >
   <IssueTitleBar
     issue={
@@ -86,8 +85,7 @@ exports[`should render issues correctly 1`] = `
   className="issue selected"
   data-issue="AVsae-CQS-9G3txfbFN2"
   onClick={[Function]}
-  role="listitem"
-  tabIndex={0}
+  role="group"
 >
   <IssueTitleBar
     issue={
index 9114453f08398f6583b26f4f54479926087d24e0..2022ebcc0d2a8e3f346c22645f06b73257f5ebac 100644 (file)
@@ -96,8 +96,8 @@ export default class IssueActionsBar extends React.PureComponent<Props, State> {
 
     return (
       <div className="issue-actions">
-        <ul className="issue-meta-list">
-          <li className="issue-meta">
+        <div className="issue-meta-list">
+          <div className="issue-meta">
             <IssueType
               canSetType={canSetType}
               isOpen={this.props.currentPopup === 'set-type' && canSetType}
@@ -105,9 +105,9 @@ export default class IssueActionsBar extends React.PureComponent<Props, State> {
               setIssueProperty={this.setIssueProperty}
               togglePopup={this.props.togglePopup}
             />
-          </li>
+          </div>
           {!isSecurityHotspot && (
-            <li className="issue-meta">
+            <div className="issue-meta">
               <IssueSeverity
                 canSetSeverity={canSetSeverity}
                 isOpen={this.props.currentPopup === 'set-severity' && canSetSeverity}
@@ -115,9 +115,9 @@ export default class IssueActionsBar extends React.PureComponent<Props, State> {
                 setIssueProperty={this.setIssueProperty}
                 togglePopup={this.props.togglePopup}
               />
-            </li>
+            </div>
           )}
-          <li className="issue-meta">
+          <div className="issue-meta">
             <IssueTransition
               hasTransitions={hasTransitions}
               isOpen={this.props.currentPopup === 'transition' && hasTransitions}
@@ -125,8 +125,8 @@ export default class IssueActionsBar extends React.PureComponent<Props, State> {
               onChange={this.handleTransition}
               togglePopup={this.props.togglePopup}
             />
-          </li>
-          <li className="issue-meta">
+          </div>
+          <div className="issue-meta">
             <IssueAssign
               canAssign={canAssign}
               isOpen={this.props.currentPopup === 'assign' && canAssign}
@@ -134,13 +134,13 @@ export default class IssueActionsBar extends React.PureComponent<Props, State> {
               onAssign={this.props.onAssign}
               togglePopup={this.props.togglePopup}
             />
-          </li>
+          </div>
           {!isSecurityHotspot && issue.effort && (
-            <li className="issue-meta">
+            <div className="issue-meta">
               <span className="issue-meta-label">
                 {translateWithParameters('issue.x_effort', issue.effort)}
               </span>
-            </li>
+            </div>
           )}
           {canComment && (
             <IssueCommentAction
@@ -152,9 +152,9 @@ export default class IssueActionsBar extends React.PureComponent<Props, State> {
               toggleComment={this.toggleComment}
             />
           )}
-        </ul>
-        <ul className="list-inline">
-          <li className="issue-meta js-issue-tags">
+        </div>
+        <div className="list-inline">
+          <div className="issue-meta js-issue-tags">
             <IssueTags
               canSetTags={canSetTags}
               isOpen={this.props.currentPopup === 'edit-tags' && canSetTags}
@@ -162,8 +162,8 @@ export default class IssueActionsBar extends React.PureComponent<Props, State> {
               onChange={this.props.onChange}
               togglePopup={this.props.togglePopup}
             />
-          </li>
-        </ul>
+          </div>
+        </div>
       </div>
     );
   }
index 102a7938c45f68a43b6d8b2ae3979137c061f843..afc63537aab251911736d51278f851e094cf785b 100644 (file)
@@ -53,7 +53,7 @@ export default class IssueAssign extends React.PureComponent<Props> {
             <Avatar
               className="little-spacer-right"
               hash={issue.assigneeAvatar}
-              name={issue.assigneeName || issue.assignee}
+              name={assigneeName}
               size={16}
             />
           </span>
@@ -70,15 +70,27 @@ export default class IssueAssign extends React.PureComponent<Props> {
   }
 
   render() {
-    if (this.props.canAssign) {
+    const { canAssign, isOpen, issue } = this.props;
+    const assigneeName = issue.assigneeName || issue.assignee;
+
+    if (canAssign) {
       return (
         <div className="dropdown">
           <Toggler
             closeOnEscape={true}
             onRequestClose={this.handleClose}
-            open={this.props.isOpen && this.props.canAssign}
+            open={isOpen}
             overlay={<SetAssigneePopup onSelect={this.props.onAssign} />}>
             <ButtonLink
+              aria-expanded={isOpen}
+              aria-label={
+                assigneeName
+                  ? translateWithParameters(
+                      'issue.assign.assigned_to_x_click_to_change',
+                      assigneeName
+                    )
+                  : translate('issue.assign.unassigned_click_to_assign')
+              }
               className="issue-action issue-action-with-options js-issue-assign"
               onClick={this.toggleAssign}>
               {this.renderAssignee()}
@@ -87,8 +99,8 @@ export default class IssueAssign extends React.PureComponent<Props> {
           </Toggler>
         </div>
       );
-    } else {
-      return this.renderAssignee();
     }
+
+    return this.renderAssignee();
   }
 }
index 3153896e929faac2f800cfec69c4a02a330c8442..15261cc7e770baf99685eb791387175e00591377 100644 (file)
@@ -52,6 +52,7 @@ export default class IssueChangelog extends React.PureComponent<Props> {
           open={this.props.isOpen}
           overlay={<ChangelogPopup issue={this.props.issue} />}>
           <ButtonLink
+            aria-expanded={this.props.isOpen}
             className="issue-action issue-action-with-options js-issue-show-changelog"
             onClick={this.handleClick}>
             <span className="issue-meta-label">
index 15ecb7b78b8209e8f8854a7158b7827e58590496..d5826942b9c57d4a7c36b54ab7c41988fa71fa16 100644 (file)
@@ -63,7 +63,11 @@ export default class IssueCommentAction extends React.PureComponent<Props> {
               toggleComment={this.props.toggleComment}
             />
           }>
-          <ButtonLink className="issue-action js-issue-comment" onClick={this.handleCommentClick}>
+          <ButtonLink
+            aria-expanded={this.props.currentPopup === 'comment'}
+            aria-label={translate('issue.comment.add_comment')}
+            className="issue-action js-issue-comment"
+            onClick={this.handleCommentClick}>
             <span className="issue-meta-label">{translate('issue.comment.formlink')}</span>
           </ButtonLink>
         </Toggler>
index 2efd3583e1abe73d95b2fdfa0903cf7edd39283b..84a617ccd88b5e7a4ef4e348d4d6517b4c19d412 100644 (file)
@@ -23,7 +23,7 @@ import { DeleteButton, EditButton } from 'sonar-ui-common/components/controls/bu
 import Toggler from 'sonar-ui-common/components/controls/Toggler';
 import DateFromNow from 'sonar-ui-common/components/intl/DateFromNow';
 import { PopupPlacement } from 'sonar-ui-common/components/ui/popups';
-import { translateWithParameters } from 'sonar-ui-common/helpers/l10n';
+import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n';
 import Avatar from '../../ui/Avatar';
 import CommentDeletePopup from '../popups/CommentDeletePopup';
 import CommentPopup from '../popups/CommentPopup';
@@ -99,6 +99,7 @@ export default class IssueCommentLine extends React.PureComponent<Props, State>
           dangerouslySetInnerHTML={{ __html: sanitize(comment.htmlText) }}
         />
         <div className="issue-comment-age">
+          <span className="a11y-hidden">{translate('issue.comment.posted_on')}</span>
           <DateFromNow date={comment.createdAt} />
         </div>
         <div className="issue-comment-actions">
@@ -118,6 +119,7 @@ export default class IssueCommentLine extends React.PureComponent<Props, State>
                   />
                 }>
                 <EditButton
+                  aria-label={translate('issue.comment.edit')}
                   className="js-issue-comment-edit button-small"
                   onClick={this.toggleEditPopup}
                 />
@@ -131,6 +133,7 @@ export default class IssueCommentLine extends React.PureComponent<Props, State>
                 open={this.state.openPopup === 'delete'}
                 overlay={<CommentDeletePopup onDelete={this.handleDelete} />}>
                 <DeleteButton
+                  aria-label={translate('issue.comment.delete')}
                   className="js-issue-comment-delete button-small"
                   onClick={this.toggleDeletePopup}
                 />
index 547c4e74bfb7683b6c2080a9429bbc1401c0dc6a..c6996fd066c709fac13edb9f3a13dbb528d889fe 100644 (file)
@@ -21,6 +21,7 @@ import * as React from 'react';
 import { ButtonLink } from 'sonar-ui-common/components/controls/buttons';
 import Toggler from 'sonar-ui-common/components/controls/Toggler';
 import DropdownIcon from 'sonar-ui-common/components/icons/DropdownIcon';
+import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n';
 import { setIssueSeverity } from '../../../api/issues';
 import { IssueResponse } from '../../../types/issues';
 import SeverityHelper from '../../shared/SeverityHelper';
@@ -62,6 +63,11 @@ export default class IssueSeverity extends React.PureComponent<Props> {
             open={this.props.isOpen && this.props.canSetSeverity}
             overlay={<SetSeverityPopup issue={issue} onSelect={this.setSeverity} />}>
             <ButtonLink
+              aria-label={translateWithParameters(
+                'issue.severity.severity_x_click_to_change',
+                translate('severity', issue.severity)
+              )}
+              aria-expanded={this.props.isOpen}
               className="issue-action issue-action-with-options js-issue-set-severity"
               onClick={this.toggleSetSeverity}>
               <SeverityHelper className="issue-meta-label" severity={issue.severity} />
@@ -70,8 +76,8 @@ export default class IssueSeverity extends React.PureComponent<Props> {
           </Toggler>
         </div>
       );
-    } else {
-      return <SeverityHelper className="issue-meta-label" severity={issue.severity} />;
     }
+
+    return <SeverityHelper className="issue-meta-label" severity={issue.severity} />;
   }
 }
index 5def1b1f573e62fea73a4d6d140b7fd824e7ea25..553bb8c6e809c76589a51155106c022df006233f 100644 (file)
@@ -66,6 +66,7 @@ export default class IssueTags extends React.PureComponent<Props> {
             open={this.props.isOpen}
             overlay={<SetIssueTagsPopup selectedTags={tags} setTags={this.setTags} />}>
             <ButtonLink
+              aria-expanded={this.props.isOpen}
               className="issue-action issue-action-with-options js-issue-edit-tags"
               onClick={this.toggleSetTags}>
               <TagsList
@@ -78,14 +79,14 @@ export default class IssueTags extends React.PureComponent<Props> {
           </Toggler>
         </div>
       );
-    } else {
-      return (
-        <TagsList
-          allowUpdate={this.props.canSetTags}
-          className="note"
-          tags={issue.tags && issue.tags.length > 0 ? issue.tags : [translate('issue.no_tag')]}
-        />
-      );
     }
+
+    return (
+      <TagsList
+        allowUpdate={this.props.canSetTags}
+        className="note"
+        tags={issue.tags && issue.tags.length > 0 ? issue.tags : [translate('issue.no_tag')]}
+      />
+    );
   }
 }
index fa867df430959dfd9f635d25d8a8c2bbe3feb198..1b82ac169b2696b730b05643907016dc924dcd46 100644 (file)
@@ -91,24 +91,24 @@ export default function IssueTitleBar(props: IssueTitleBarProps) {
       </WorkspaceContext.Consumer>
 
       <div className="issue-row-meta">
-        <ul className="issue-meta-list">
-          <li className="issue-meta">
+        <div className="issue-meta-list">
+          <div className="issue-meta">
             <IssueChangelog
               creationDate={issue.creationDate}
               isOpen={props.currentPopup === 'changelog'}
               issue={issue}
               togglePopup={props.togglePopup}
             />
-          </li>
+          </div>
           {issue.textRange != null && (
-            <li className="issue-meta">
+            <div className="issue-meta">
               <span className="issue-meta-label" title={translate('line_number')}>
                 L{issue.textRange.endLine}
               </span>
-            </li>
+            </div>
           )}
           {displayLocations && (
-            <li className="issue-meta">
+            <div className="issue-meta">
               {props.displayLocationsLink ? (
                 <Link target="_blank" to={issueUrl}>
                   {locationsBadge}
@@ -116,9 +116,9 @@ export default function IssueTitleBar(props: IssueTitleBarProps) {
               ) : (
                 locationsBadge
               )}
-            </li>
+            </div>
           )}
-          <li className="issue-meta">
+          <div className="issue-meta">
             <Link
               className="js-issue-permalink link-no-underline"
               target="_blank"
@@ -126,18 +126,18 @@ export default function IssueTitleBar(props: IssueTitleBarProps) {
               to={issueUrl}>
               <LinkIcon />
             </Link>
-          </li>
+          </div>
           {hasSimilarIssuesFilter && (
-            <li className="issue-meta">
+            <div className="issue-meta">
               <SimilarIssuesFilter
                 isOpen={props.currentPopup === 'similarIssues'}
                 issue={issue}
                 onFilter={props.onFilter}
                 togglePopup={props.togglePopup}
               />
-            </li>
+            </div>
           )}
-        </ul>
+        </div>
       </div>
     </div>
   );
index e0e11f20ada416d87744beea9d783b84e99165d6..419eaa2b92dc01e85d3822fa89c7725b6bc3287e 100644 (file)
@@ -21,6 +21,7 @@ import * as React from 'react';
 import { ButtonLink } from 'sonar-ui-common/components/controls/buttons';
 import Toggler from 'sonar-ui-common/components/controls/Toggler';
 import DropdownIcon from 'sonar-ui-common/components/icons/DropdownIcon';
+import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n';
 import { setIssueTransition } from '../../../api/issues';
 import StatusHelper from '../../shared/StatusHelper';
 import { updateIssue } from '../actions';
@@ -69,6 +70,11 @@ export default class IssueTransition extends React.PureComponent<Props> {
               />
             }>
             <ButtonLink
+              aria-label={translateWithParameters(
+                'issue.transition.status_x_click_to_change',
+                translate('issue.status', issue.status)
+              )}
+              aria-expanded={this.props.isOpen}
               className="issue-action issue-action-with-options js-issue-transition"
               onClick={this.toggleSetTransition}>
               <StatusHelper
@@ -81,14 +87,14 @@ export default class IssueTransition extends React.PureComponent<Props> {
           </Toggler>
         </div>
       );
-    } else {
-      return (
-        <StatusHelper
-          className="issue-meta-label"
-          resolution={issue.resolution}
-          status={issue.status}
-        />
-      );
     }
+
+    return (
+      <StatusHelper
+        className="issue-meta-label"
+        resolution={issue.resolution}
+        status={issue.status}
+      />
+    );
   }
 }
index 0b1f85ed35797f4ae85abf9e31f09dc1b67f21f9..d2d4871f289db6b52d8f5597d4de636c9aa0a84b 100644 (file)
@@ -22,7 +22,7 @@ import { ButtonLink } from 'sonar-ui-common/components/controls/buttons';
 import Toggler from 'sonar-ui-common/components/controls/Toggler';
 import DropdownIcon from 'sonar-ui-common/components/icons/DropdownIcon';
 import IssueTypeIcon from 'sonar-ui-common/components/icons/IssueTypeIcon';
-import { translate } from 'sonar-ui-common/helpers/l10n';
+import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n';
 import { setIssueType } from '../../../api/issues';
 import { colors } from '../../../app/theme';
 import { IssueResponse } from '../../../types/issues';
@@ -64,6 +64,11 @@ export default class IssueType extends React.PureComponent<Props> {
             open={this.props.isOpen && this.props.canSetType}
             overlay={<SetTypePopup issue={issue} onSelect={this.setType} />}>
             <ButtonLink
+              aria-label={translateWithParameters(
+                'issue.type.type_x_click_to_change',
+                translate('issue.type', issue.type)
+              )}
+              aria-expanded={this.props.isOpen}
               className="issue-action issue-action-with-options js-issue-set-type"
               onClick={this.toggleSetType}>
               <IssueTypeIcon
@@ -77,13 +82,13 @@ export default class IssueType extends React.PureComponent<Props> {
           </Toggler>
         </div>
       );
-    } else {
-      return (
-        <span>
-          <IssueTypeIcon className="little-spacer-right" query={issue.type} />
-          {translate('issue.type', issue.type)}
-        </span>
-      );
     }
+
+    return (
+      <span>
+        <IssueTypeIcon className="little-spacer-right" query={issue.type} />
+        {translate('issue.type', issue.type)}
+      </span>
+    );
   }
 }
index b4234c3bdefc9805f47f43f2a995ed91d6c55e23..3292efc5f68a0ff5fa2ef05cc0f1f7053bd2d496 100644 (file)
@@ -57,6 +57,7 @@ export default class SimilarIssuesFilter extends React.PureComponent<Props> {
           overlay={<SimilarIssuesPopup issue={this.props.issue} onFilter={this.handleFilter} />}>
           <ButtonLink
             aria-label={translate('issue.filter_similar_issues')}
+            aria-expanded={this.props.isOpen}
             className="issue-action issue-action-with-options js-issue-filter"
             onClick={this.togglePopup}
             title={translate('issue.filter_similar_issues')}>
index a63ade647303be2808dee87f36bdf334f629f4cf..574359b316b57119da2099d945577ba0ce6d5fb8 100644 (file)
@@ -4,10 +4,10 @@ exports[`should render commentable correctly 1`] = `
 <div
   className="issue-actions"
 >
-  <ul
+  <div
     className="issue-meta-list"
   >
-    <li
+    <div
       className="issue-meta"
     >
       <IssueType
@@ -49,8 +49,8 @@ exports[`should render commentable correctly 1`] = `
         setIssueProperty={[Function]}
         togglePopup={[MockFunction]}
       />
-    </li>
-    <li
+    </div>
+    <div
       className="issue-meta"
     >
       <IssueSeverity
@@ -92,8 +92,8 @@ exports[`should render commentable correctly 1`] = `
         setIssueProperty={[Function]}
         togglePopup={[MockFunction]}
       />
-    </li>
-    <li
+    </div>
+    <div
       className="issue-meta"
     >
       <IssueTransition
@@ -135,8 +135,8 @@ exports[`should render commentable correctly 1`] = `
         onChange={[Function]}
         togglePopup={[MockFunction]}
       />
-    </li>
-    <li
+    </div>
+    <div
       className="issue-meta"
     >
       <IssueAssign
@@ -178,7 +178,7 @@ exports[`should render commentable correctly 1`] = `
         onAssign={[MockFunction]}
         togglePopup={[MockFunction]}
       />
-    </li>
+    </div>
     <IssueCommentAction
       commentAutoTriggered={false}
       commentPlaceholder=""
@@ -186,11 +186,11 @@ exports[`should render commentable correctly 1`] = `
       onChange={[MockFunction]}
       toggleComment={[Function]}
     />
-  </ul>
-  <ul
+  </div>
+  <div
     className="list-inline"
   >
-    <li
+    <div
       className="issue-meta js-issue-tags"
     >
       <IssueTags
@@ -232,8 +232,8 @@ exports[`should render commentable correctly 1`] = `
         onChange={[MockFunction]}
         togglePopup={[MockFunction]}
       />
-    </li>
-  </ul>
+    </div>
+  </div>
 </div>
 `;
 
@@ -241,10 +241,10 @@ exports[`should render effort correctly 1`] = `
 <div
   className="issue-actions"
 >
-  <ul
+  <div
     className="issue-meta-list"
   >
-    <li
+    <div
       className="issue-meta"
     >
       <IssueType
@@ -285,8 +285,8 @@ exports[`should render effort correctly 1`] = `
         setIssueProperty={[Function]}
         togglePopup={[MockFunction]}
       />
-    </li>
-    <li
+    </div>
+    <div
       className="issue-meta"
     >
       <IssueSeverity
@@ -327,8 +327,8 @@ exports[`should render effort correctly 1`] = `
         setIssueProperty={[Function]}
         togglePopup={[MockFunction]}
       />
-    </li>
-    <li
+    </div>
+    <div
       className="issue-meta"
     >
       <IssueTransition
@@ -369,8 +369,8 @@ exports[`should render effort correctly 1`] = `
         onChange={[Function]}
         togglePopup={[MockFunction]}
       />
-    </li>
-    <li
+    </div>
+    <div
       className="issue-meta"
     >
       <IssueAssign
@@ -411,8 +411,8 @@ exports[`should render effort correctly 1`] = `
         onAssign={[MockFunction]}
         togglePopup={[MockFunction]}
       />
-    </li>
-    <li
+    </div>
+    <div
       className="issue-meta"
     >
       <span
@@ -420,12 +420,12 @@ exports[`should render effort correctly 1`] = `
       >
         issue.x_effort.great
       </span>
-    </li>
-  </ul>
-  <ul
+    </div>
+  </div>
+  <div
     className="list-inline"
   >
-    <li
+    <div
       className="issue-meta js-issue-tags"
     >
       <IssueTags
@@ -466,8 +466,8 @@ exports[`should render effort correctly 1`] = `
         onChange={[MockFunction]}
         togglePopup={[MockFunction]}
       />
-    </li>
-  </ul>
+    </div>
+  </div>
 </div>
 `;
 
@@ -475,10 +475,10 @@ exports[`should render issue correctly 1`] = `
 <div
   className="issue-actions"
 >
-  <ul
+  <div
     className="issue-meta-list"
   >
-    <li
+    <div
       className="issue-meta"
     >
       <IssueType
@@ -518,8 +518,8 @@ exports[`should render issue correctly 1`] = `
         setIssueProperty={[Function]}
         togglePopup={[MockFunction]}
       />
-    </li>
-    <li
+    </div>
+    <div
       className="issue-meta"
     >
       <IssueSeverity
@@ -559,8 +559,8 @@ exports[`should render issue correctly 1`] = `
         setIssueProperty={[Function]}
         togglePopup={[MockFunction]}
       />
-    </li>
-    <li
+    </div>
+    <div
       className="issue-meta"
     >
       <IssueTransition
@@ -600,8 +600,8 @@ exports[`should render issue correctly 1`] = `
         onChange={[Function]}
         togglePopup={[MockFunction]}
       />
-    </li>
-    <li
+    </div>
+    <div
       className="issue-meta"
     >
       <IssueAssign
@@ -641,12 +641,12 @@ exports[`should render issue correctly 1`] = `
         onAssign={[MockFunction]}
         togglePopup={[MockFunction]}
       />
-    </li>
-  </ul>
-  <ul
+    </div>
+  </div>
+  <div
     className="list-inline"
   >
-    <li
+    <div
       className="issue-meta js-issue-tags"
     >
       <IssueTags
@@ -686,8 +686,8 @@ exports[`should render issue correctly 1`] = `
         onChange={[MockFunction]}
         togglePopup={[MockFunction]}
       />
-    </li>
-  </ul>
+    </div>
+  </div>
 </div>
 `;
 
@@ -695,10 +695,10 @@ exports[`should render security hotspot correctly 1`] = `
 <div
   className="issue-actions"
 >
-  <ul
+  <div
     className="issue-meta-list"
   >
-    <li
+    <div
       className="issue-meta"
     >
       <IssueType
@@ -738,8 +738,8 @@ exports[`should render security hotspot correctly 1`] = `
         setIssueProperty={[Function]}
         togglePopup={[MockFunction]}
       />
-    </li>
-    <li
+    </div>
+    <div
       className="issue-meta"
     >
       <IssueTransition
@@ -779,8 +779,8 @@ exports[`should render security hotspot correctly 1`] = `
         onChange={[Function]}
         togglePopup={[MockFunction]}
       />
-    </li>
-    <li
+    </div>
+    <div
       className="issue-meta"
     >
       <IssueAssign
@@ -820,12 +820,12 @@ exports[`should render security hotspot correctly 1`] = `
         onAssign={[MockFunction]}
         togglePopup={[MockFunction]}
       />
-    </li>
-  </ul>
-  <ul
+    </div>
+  </div>
+  <div
     className="list-inline"
   >
-    <li
+    <div
       className="issue-meta js-issue-tags"
     >
       <IssueTags
@@ -865,7 +865,7 @@ exports[`should render security hotspot correctly 1`] = `
         onChange={[MockFunction]}
         togglePopup={[MockFunction]}
       />
-    </li>
-  </ul>
+    </div>
+  </div>
 </div>
 `;
index 7f3fc48c7c20e84c83ca2b1d5459f4e7b2ed6f4f..8496a984f30787e424e6c78ca8bf57945ce0bacd 100644 (file)
@@ -24,6 +24,8 @@ exports[`should open the popup when the button is clicked 2`] = `
     }
   >
     <ButtonLink
+      aria-expanded={true}
+      aria-label="issue.assign.assigned_to_x_click_to_change.John Doe"
       className="issue-action issue-action-with-options js-issue-assign"
       onClick={[Function]}
     >
@@ -65,6 +67,8 @@ exports[`should render a fallback assignee display if assignee info are not avai
     }
   >
     <ButtonLink
+      aria-expanded={false}
+      aria-label="issue.assign.unassigned_click_to_assign"
       className="issue-action issue-action-with-options js-issue-assign"
       onClick={[Function]}
     >
@@ -96,6 +100,8 @@ exports[`should render with the action 1`] = `
     }
   >
     <ButtonLink
+      aria-expanded={false}
+      aria-label="issue.assign.assigned_to_x_click_to_change.John Doe"
       className="issue-action issue-action-with-options js-issue-assign"
       onClick={[Function]}
     >
index 774bd2669b7c3feafb6767dc3da32b66eed3f298..dc6ab37ef514a299d6d13ac299cf77e59c69ab23 100644 (file)
@@ -29,6 +29,7 @@ exports[`should open the popup when the button is clicked 2`] = `
     }
   >
     <ButtonLink
+      aria-expanded={true}
       className="issue-action issue-action-with-options js-issue-show-changelog"
       onClick={[Function]}
     >
@@ -67,6 +68,7 @@ exports[`should render correctly 1`] = `
     }
   >
     <ButtonLink
+      aria-expanded={false}
       className="issue-action issue-action-with-options js-issue-show-changelog"
       onClick={[Function]}
     >
index dfb462c293fdd413a1812c86893fbf289d848ed6..b83e932d61f2bce330f2982e101fb9d8098a677c 100644 (file)
@@ -29,6 +29,8 @@ exports[`should open the popup when the button is clicked 1`] = `
     }
   >
     <ButtonLink
+      aria-expanded={true}
+      aria-label="issue.comment.add_comment"
       className="issue-action js-issue-comment"
       onClick={[Function]}
     >
@@ -59,6 +61,8 @@ exports[`should render correctly 1`] = `
     }
   >
     <ButtonLink
+      aria-expanded={false}
+      aria-label="issue.comment.add_comment"
       className="issue-action js-issue-comment"
       onClick={[Function]}
     >
index 1f5b5388ac9fa3ccb22f7c7fbda85ab32b4815d3..1a6849c827e48f6b2448e239f600449f2dcb7def 100644 (file)
@@ -39,6 +39,11 @@ exports[`should open the right popups when the buttons are clicked 3`] = `
   <div
     className="issue-comment-age"
   >
+    <span
+      className="a11y-hidden"
+    >
+      issue.comment.posted_on
+    </span>
     <DateFromNow
       date="2017-03-01T09:36:01+0100"
     />
@@ -76,6 +81,7 @@ exports[`should open the right popups when the buttons are clicked 3`] = `
         }
       >
         <EditButton
+          aria-label="issue.comment.edit"
           className="js-issue-comment-edit button-small"
           onClick={[Function]}
         />
@@ -94,6 +100,7 @@ exports[`should open the right popups when the buttons are clicked 3`] = `
         }
       >
         <DeleteButton
+          aria-label="issue.comment.delete"
           className="js-issue-comment-delete button-small"
           onClick={[Function]}
         />
@@ -130,6 +137,11 @@ exports[`should render correctly a comment that is not updatable 1`] = `
   <div
     className="issue-comment-age"
   >
+    <span
+      className="a11y-hidden"
+    >
+      issue.comment.posted_on
+    </span>
     <DateFromNow
       date="2017-03-01T09:36:01+0100"
     />
@@ -167,6 +179,11 @@ exports[`should render correctly a comment that is updatable 1`] = `
   <div
     className="issue-comment-age"
   >
+    <span
+      className="a11y-hidden"
+    >
+      issue.comment.posted_on
+    </span>
     <DateFromNow
       date="2017-03-01T09:36:01+0100"
     />
@@ -204,6 +221,7 @@ exports[`should render correctly a comment that is updatable 1`] = `
         }
       >
         <EditButton
+          aria-label="issue.comment.edit"
           className="js-issue-comment-edit button-small"
           onClick={[Function]}
         />
@@ -222,6 +240,7 @@ exports[`should render correctly a comment that is updatable 1`] = `
         }
       >
         <DeleteButton
+          aria-label="issue.comment.delete"
           className="js-issue-comment-delete button-small"
           onClick={[Function]}
         />
index 666c4bb4d4741faf372665fc44b16e060b89eec1..5d530bc6794782e8fad3037fb7cd8f672a3dee7d 100644 (file)
@@ -28,6 +28,8 @@ exports[`should open the popup when the button is clicked 2`] = `
     }
   >
     <ButtonLink
+      aria-expanded={true}
+      aria-label="issue.severity.severity_x_click_to_change.severity.BLOCKER"
       className="issue-action issue-action-with-options js-issue-set-severity"
       onClick={[Function]}
     >
@@ -62,6 +64,8 @@ exports[`should render with the action 1`] = `
     }
   >
     <ButtonLink
+      aria-expanded={false}
+      aria-label="issue.severity.severity_x_click_to_change.severity.BLOCKER"
       className="issue-action issue-action-with-options js-issue-set-severity"
       onClick={[Function]}
     >
index 001d1d6908fb69f5590929e0787bec0a3b99c3d8..106169cb019dfdb3576e214b80fa9903f4547786 100644 (file)
@@ -29,6 +29,7 @@ exports[`should open the popup when the button is clicked 2`] = `
     }
   >
     <ButtonLink
+      aria-expanded={true}
       className="issue-action issue-action-with-options js-issue-edit-tags"
       onClick={[Function]}
     >
@@ -66,6 +67,7 @@ exports[`should render with the action 1`] = `
     }
   >
     <ButtonLink
+      aria-expanded={false}
       className="issue-action issue-action-with-options js-issue-edit-tags"
       onClick={[Function]}
     >
index 2e401f7f8bdfca21ddbb15f52b28329eb51d2530..3301b5c37636a2afdaf8dfa7c91f120f70291bd2 100644 (file)
@@ -10,10 +10,10 @@ exports[`should render correctly: default 1`] = `
   <div
     className="issue-row-meta"
   >
-    <ul
+    <div
       className="issue-meta-list"
     >
-      <li
+      <div
         className="issue-meta"
       >
         <IssueChangelog
@@ -53,8 +53,8 @@ exports[`should render correctly: default 1`] = `
           }
           togglePopup={[MockFunction]}
         />
-      </li>
-      <li
+      </div>
+      <div
         className="issue-meta"
       >
         <span
@@ -64,8 +64,8 @@ exports[`should render correctly: default 1`] = `
           L
           26
         </span>
-      </li>
-      <li
+      </div>
+      <div
         className="issue-meta"
       >
         <Link
@@ -88,8 +88,8 @@ exports[`should render correctly: default 1`] = `
         >
           <LinkIcon />
         </Link>
-      </li>
-    </ul>
+      </div>
+    </div>
   </div>
 </div>
 `;
@@ -114,10 +114,10 @@ exports[`should render correctly: with filter 1`] = `
   <div
     className="issue-row-meta"
   >
-    <ul
+    <div
       className="issue-meta-list"
     >
-      <li
+      <div
         className="issue-meta"
       >
         <IssueChangelog
@@ -157,8 +157,8 @@ exports[`should render correctly: with filter 1`] = `
           }
           togglePopup={[MockFunction]}
         />
-      </li>
-      <li
+      </div>
+      <div
         className="issue-meta"
       >
         <span
@@ -168,8 +168,8 @@ exports[`should render correctly: with filter 1`] = `
           L
           26
         </span>
-      </li>
-      <li
+      </div>
+      <div
         className="issue-meta"
       >
         <Link
@@ -192,8 +192,8 @@ exports[`should render correctly: with filter 1`] = `
         >
           <LinkIcon />
         </Link>
-      </li>
-      <li
+      </div>
+      <div
         className="issue-meta"
       >
         <SimilarIssuesFilter
@@ -233,8 +233,8 @@ exports[`should render correctly: with filter 1`] = `
           onFilter={[MockFunction]}
           togglePopup={[MockFunction]}
         />
-      </li>
-    </ul>
+      </div>
+    </div>
   </div>
 </div>
 `;
@@ -249,10 +249,10 @@ exports[`should render correctly: with multi locations 1`] = `
   <div
     className="issue-row-meta"
   >
-    <ul
+    <div
       className="issue-meta-list"
     >
-      <li
+      <div
         className="issue-meta"
       >
         <IssueChangelog
@@ -360,8 +360,8 @@ exports[`should render correctly: with multi locations 1`] = `
           }
           togglePopup={[MockFunction]}
         />
-      </li>
-      <li
+      </div>
+      <div
         className="issue-meta"
       >
         <span
@@ -371,8 +371,8 @@ exports[`should render correctly: with multi locations 1`] = `
           L
           26
         </span>
-      </li>
-      <li
+      </div>
+      <div
         className="issue-meta"
       >
         <Tooltip
@@ -382,8 +382,8 @@ exports[`should render correctly: with multi locations 1`] = `
             7
           </LocationIndex>
         </Tooltip>
-      </li>
-      <li
+      </div>
+      <div
         className="issue-meta"
       >
         <Link
@@ -406,8 +406,8 @@ exports[`should render correctly: with multi locations 1`] = `
         >
           <LinkIcon />
         </Link>
-      </li>
-    </ul>
+      </div>
+    </div>
   </div>
 </div>
 `;
@@ -422,10 +422,10 @@ exports[`should render correctly: with multi locations and link 1`] = `
   <div
     className="issue-row-meta"
   >
-    <ul
+    <div
       className="issue-meta-list"
     >
-      <li
+      <div
         className="issue-meta"
       >
         <IssueChangelog
@@ -533,8 +533,8 @@ exports[`should render correctly: with multi locations and link 1`] = `
           }
           togglePopup={[MockFunction]}
         />
-      </li>
-      <li
+      </div>
+      <div
         className="issue-meta"
       >
         <span
@@ -544,8 +544,8 @@ exports[`should render correctly: with multi locations and link 1`] = `
           L
           26
         </span>
-      </li>
-      <li
+      </div>
+      <div
         className="issue-meta"
       >
         <Link
@@ -573,8 +573,8 @@ exports[`should render correctly: with multi locations and link 1`] = `
             </LocationIndex>
           </Tooltip>
         </Link>
-      </li>
-      <li
+      </div>
+      <div
         className="issue-meta"
       >
         <Link
@@ -598,8 +598,8 @@ exports[`should render correctly: with multi locations and link 1`] = `
         >
           <LinkIcon />
         </Link>
-      </li>
-    </ul>
+      </div>
+    </div>
   </div>
 </div>
 `;
index e17d30f0a596f421e2b472a3cbeb26147db6df40..d3d54e58071c4c2439709ff8cfa25564f04c9d9d 100644 (file)
@@ -33,6 +33,8 @@ exports[`should open the popup when the button is clicked 2`] = `
     }
   >
     <ButtonLink
+      aria-expanded={true}
+      aria-label="issue.transition.status_x_click_to_change.issue.status.OPEN"
       className="issue-action issue-action-with-options js-issue-transition"
       onClick={[Function]}
     >
@@ -69,6 +71,8 @@ exports[`should render with a resolution 1`] = `
     }
   >
     <ButtonLink
+      aria-expanded={false}
+      aria-label="issue.transition.status_x_click_to_change.issue.status.RESOLVED"
       className="issue-action issue-action-with-options js-issue-transition"
       onClick={[Function]}
     >
@@ -109,6 +113,8 @@ exports[`should render with the action 1`] = `
     }
   >
     <ButtonLink
+      aria-expanded={false}
+      aria-label="issue.transition.status_x_click_to_change.issue.status.OPEN"
       className="issue-action issue-action-with-options js-issue-transition"
       onClick={[Function]}
     >
index 27fc50ed8823fb0b3e32a26181f55935926f3173..b07ade8d1fdac93fd10c766fc3a8ee5b058b2c57 100644 (file)
@@ -28,6 +28,8 @@ exports[`should open the popup when the button is clicked 2`] = `
     }
   >
     <ButtonLink
+      aria-expanded={true}
+      aria-label="issue.type.type_x_click_to_change.issue.type.BUG"
       className="issue-action issue-action-with-options js-issue-set-type"
       onClick={[Function]}
     >
@@ -64,6 +66,8 @@ exports[`should render with the action 1`] = `
     }
   >
     <ButtonLink
+      aria-expanded={false}
+      aria-label="issue.type.type_x_click_to_change.issue.type.BUG"
       className="issue-action issue-action-with-options js-issue-set-type"
       onClick={[Function]}
     >
index fa3795f1dc0cb702434fb98ed250854ee9a13726..b97f6a7e60dc66f31c73e41ce61e71ba9d883d34 100644 (file)
@@ -762,14 +762,21 @@ hotspots.update.success=Security Hotspot status was successfully changed to {0}
 #
 #------------------------------------------------------------------------------
 
+issues.on_file_x=Issues on file {0}
 issue.add_tags=Add Tags
 issue.remove_tags=Remove Tags
 issue.no_tag=No tags
+issue.assign.assigned_to_x_click_to_change=Assigned to {0}, click to change
+issue.assign.unassigned_click_to_assign=Unassigned, click to assign issue
 issue.assign.formlink=Assign
 issue.assign.to_me=to me
+issue.comment.add_comment=Add Comment
 issue.comment.formlink=Comment
 issue.comment.submit=Comment
 issue.comment.explain_why=Consider explaining why
+issue.comment.posted_on=Comment posted on
+issue.comment.edit=Edit comment
+issue.comment.delete=Delete comment
 issue.comment.delete_confirm_message=Do you want to delete this comment?
 issue.manual_vulnerability=Manual
 issue.manual_vulnerability.description=This Vulnerability was created from a Security Hotspot and has its own issue workflow.
@@ -777,6 +784,9 @@ issue.rule_details=Rule Details
 issue.send_notifications=Send Notifications
 issue.why_this_issue=Why is this an issue?
 issue.why_this_issue.long=Why is this an issue? Open the rule's details at the bottom of the page.
+issue.type.type_x_click_to_change=Type: {0}, click to change
+issue.severity.severity_x_click_to_change=Severity: {0}, click to change
+issue.transition.status_x_click_to_change=Issue status: {0}, click to change
 issue.transition=Transition
 issue.transition.confirm=Confirm
 issue.transition.confirm.description=This issue has been reviewed and something should be done eventually to handle it.