aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--server/sonar-web/src/main/js/api/mocks/IssuesServiceMock.ts29
-rw-r--r--server/sonar-web/src/main/js/apps/issues/__tests__/IssueApp-it.tsx15
-rw-r--r--server/sonar-web/src/main/js/apps/issues/components/IssueHeader.tsx57
-rw-r--r--server/sonar-web/src/main/js/components/issue/components/IssueMessage.tsx57
-rw-r--r--server/sonar-web/src/main/js/components/issue/components/IssueMessageTags.tsx91
-rw-r--r--server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueMessage-test.tsx.snap84
-rw-r--r--server/sonar-web/src/main/js/types/issues.ts2
7 files changed, 167 insertions, 168 deletions
diff --git a/server/sonar-web/src/main/js/api/mocks/IssuesServiceMock.ts b/server/sonar-web/src/main/js/api/mocks/IssuesServiceMock.ts
index d0e9abd2661..ac260fff216 100644
--- a/server/sonar-web/src/main/js/api/mocks/IssuesServiceMock.ts
+++ b/server/sonar-web/src/main/js/api/mocks/IssuesServiceMock.ts
@@ -214,6 +214,35 @@ export default class IssuesServiceMock {
],
'component.key'
)
+ },
+ {
+ issue: mockRawIssue(false, {
+ actions: ['set_type', 'set_tags', 'comment', 'set_severity', 'assign'],
+ transitions: ['confirm', 'resolve', 'falsepositive', 'wontfix'],
+ key: 'issue4',
+ component: 'project:file.bar',
+ message: 'Issue with tags',
+ rule: 'external_eslint_repo:no-div-regex',
+ textRange: {
+ startLine: 25,
+ endLine: 25,
+ startOffset: 0,
+ endOffset: 1
+ },
+ ruleDescriptionContextKey: 'spring',
+ ruleStatus: 'DEPRECATED',
+ quickFixAvailable: true
+ }),
+ snippets: keyBy(
+ [
+ mockSnippetsByComponent(
+ 'file.bar',
+ 'project',
+ times(40, i => i + 20)
+ )
+ ],
+ 'component.key'
+ )
}
];
(searchIssues as jest.Mock).mockImplementation(this.handleSearchIssues);
diff --git a/server/sonar-web/src/main/js/apps/issues/__tests__/IssueApp-it.tsx b/server/sonar-web/src/main/js/apps/issues/__tests__/IssueApp-it.tsx
index 2746ea8e931..0d3a43a18b8 100644
--- a/server/sonar-web/src/main/js/apps/issues/__tests__/IssueApp-it.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/__tests__/IssueApp-it.tsx
@@ -446,6 +446,21 @@ it('should show code tabs when any secondary location is selected', async () =>
).toBeInTheDocument();
});
+it('should show issue tags if applicable', async () => {
+ const user = userEvent.setup();
+ handler.setIsAdmin(true);
+ renderIssueApp();
+
+ // Select an issue with an advanced rule
+ await user.click(await screen.findByRole('region', { name: 'Issue with tags' }));
+
+ expect(
+ screen.getByRole('heading', {
+ name: 'Issue with tags sonar-lint-icon issue.resolution.badge.DEPRECATED'
+ })
+ ).toBeInTheDocument();
+});
+
describe('redirects', () => {
it('should work for hotspots', () => {
renderProjectIssuesApp(`project/issues?types=${IssueType.SecurityHotspot}`);
diff --git a/server/sonar-web/src/main/js/apps/issues/components/IssueHeader.tsx b/server/sonar-web/src/main/js/apps/issues/components/IssueHeader.tsx
index 61eda47277e..630c044e89f 100644
--- a/server/sonar-web/src/main/js/apps/issues/components/IssueHeader.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/components/IssueHeader.tsx
@@ -18,20 +18,17 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { FormattedMessage } from 'react-intl';
import { Link } from 'react-router-dom';
import { setIssueAssignee } from '../../../api/issues';
-import DocumentationTooltip from '../../../components/common/DocumentationTooltip';
-import Tooltip from '../../../components/controls/Tooltip';
import LinkIcon from '../../../components/icons/LinkIcon';
-import SonarLintIcon from '../../../components/icons/SonarLintIcon';
import { updateIssue } from '../../../components/issue/actions';
import IssueActionsBar from '../../../components/issue/components/IssueActionsBar';
import IssueChangelog from '../../../components/issue/components/IssueChangelog';
+import IssueMessageTags from '../../../components/issue/components/IssueMessageTags';
import { getBranchLikeQuery } from '../../../helpers/branch-like';
import { isInput, isShortcut } from '../../../helpers/keyboardEventHelpers';
import { KeyboardKeys } from '../../../helpers/keycodes';
-import { translate, translateWithParameters } from '../../../helpers/l10n';
+import { translate } from '../../../helpers/l10n';
import { getComponentIssuesUrl, getRuleUrl } from '../../../helpers/urls';
import { BranchLike } from '../../../types/branch-like';
import { RuleStatus } from '../../../types/rules';
@@ -132,56 +129,18 @@ export default class IssueHeader extends React.PureComponent<Props, State> {
types: issue.type === 'SECURITY_HOTSPOT' ? issue.type : undefined
});
const ruleStatus = issue.ruleStatus as RuleStatus | undefined;
- const ruleEngine = issue.externalRuleEngine;
const { quickFixAvailable } = issue;
return (
<>
<div className="display-flex-center display-flex-space-between big-padded-top">
<h1 className="text-bold spacer-right">
- {issue.message}
- {quickFixAvailable && (
- <Tooltip
- overlay={
- <FormattedMessage
- id="issue.quick_fix_available_with_sonarlint"
- defaultMessage={translate('issue.quick_fix_available_with_sonarlint')}
- values={{
- link: (
- <a
- href="https://www.sonarqube.org/sonarlint/?referrer=sonarqube-quick-fix"
- rel="noopener noreferrer"
- target="_blank">
- SonarLint
- </a>
- )
- }}
- />
- }
- mouseLeaveDelay={0.5}>
- <SonarLintIcon className="it__issues-sonarlint-quick-fix spacer-left" size={20} />
- </Tooltip>
- )}
- {(ruleStatus === RuleStatus.Deprecated || ruleStatus === RuleStatus.Removed) && (
- <DocumentationTooltip
- content={translate('rules.status', ruleStatus, 'help')}
- links={[
- {
- href: '/documentation/user-guide/rules/',
- label: translateWithParameters('see_x', translate('rules'))
- }
- ]}>
- <span className="badge spacer-left badge-error">
- {translate('issue.resolution.badge', ruleStatus)}
- </span>
- </DocumentationTooltip>
- )}
- {ruleEngine && (
- <Tooltip
- overlay={translateWithParameters('issue.from_external_rule_engine', ruleEngine)}>
- <div className="badge spacer-left text-baseline">{ruleEngine}</div>
- </Tooltip>
- )}
+ <span className="spacer-right">{issue.message}</span>
+ <IssueMessageTags
+ engine={issue.externalRuleEngine}
+ quickFixAvailable={quickFixAvailable}
+ ruleStatus={ruleStatus}
+ />
</h1>
<div className="issue-meta issue-get-perma-link">
<Link
diff --git a/server/sonar-web/src/main/js/components/issue/components/IssueMessage.tsx b/server/sonar-web/src/main/js/components/issue/components/IssueMessage.tsx
index 0a605f6546b..df6c0798b3c 100644
--- a/server/sonar-web/src/main/js/components/issue/components/IssueMessage.tsx
+++ b/server/sonar-web/src/main/js/components/issue/components/IssueMessage.tsx
@@ -18,14 +18,11 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { FormattedMessage } from 'react-intl';
import { ButtonLink } from '../../../components/controls/buttons';
-import Tooltip from '../../../components/controls/Tooltip';
-import { translate, translateWithParameters } from '../../../helpers/l10n';
+import { translate } from '../../../helpers/l10n';
import { RuleStatus } from '../../../types/rules';
-import DocumentationTooltip from '../../common/DocumentationTooltip';
-import SonarLintIcon from '../../icons/SonarLintIcon';
import { WorkspaceContext } from '../../workspace/context';
+import IssueMessageTags from './IssueMessageTags';
export interface IssueMessageProps {
engine?: string;
@@ -46,55 +43,17 @@ export default function IssueMessage(props: IssueMessageProps) {
displayWhyIsThisAnIssue
} = props;
- const { externalRulesRepoNames, openRule } = React.useContext(WorkspaceContext);
- const ruleEngine = (engine && externalRulesRepoNames && externalRulesRepoNames[engine]) || engine;
+ const { openRule } = React.useContext(WorkspaceContext);
return (
<>
<div className="display-inline-flex-center issue-message break-word">
<span className="spacer-right">{message}</span>
- {quickFixAvailable && (
- <Tooltip
- overlay={
- <FormattedMessage
- id="issue.quick_fix_available_with_sonarlint"
- defaultMessage={translate('issue.quick_fix_available_with_sonarlint')}
- values={{
- link: (
- <a
- href="https://www.sonarqube.org/sonarlint/?referrer=sonarqube-quick-fix"
- rel="noopener noreferrer"
- target="_blank">
- SonarLint
- </a>
- )
- }}
- />
- }
- mouseLeaveDelay={0.5}>
- <SonarLintIcon className="it__issues-sonarlint-quick-fix spacer-right" size={15} />
- </Tooltip>
- )}
- {ruleStatus && (ruleStatus === RuleStatus.Deprecated || ruleStatus === RuleStatus.Removed) && (
- <DocumentationTooltip
- className="spacer-left"
- content={translate('rules.status', ruleStatus, 'help')}
- links={[
- {
- href: '/documentation/user-guide/rules/',
- label: translateWithParameters('see_x', translate('rules'))
- }
- ]}>
- <span className="spacer-right badge badge-error">
- {translate('issue.resolution.badge', ruleStatus)}
- </span>
- </DocumentationTooltip>
- )}
- {ruleEngine && (
- <Tooltip overlay={translateWithParameters('issue.from_external_rule_engine', ruleEngine)}>
- <div className="badge spacer-right text-baseline">{ruleEngine}</div>
- </Tooltip>
- )}
+ <IssueMessageTags
+ engine={engine}
+ quickFixAvailable={quickFixAvailable}
+ ruleStatus={ruleStatus}
+ />
</div>
{displayWhyIsThisAnIssue && (
<ButtonLink
diff --git a/server/sonar-web/src/main/js/components/issue/components/IssueMessageTags.tsx b/server/sonar-web/src/main/js/components/issue/components/IssueMessageTags.tsx
new file mode 100644
index 00000000000..f681a078916
--- /dev/null
+++ b/server/sonar-web/src/main/js/components/issue/components/IssueMessageTags.tsx
@@ -0,0 +1,91 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 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 * as React from 'react';
+import { FormattedMessage } from 'react-intl';
+import Tooltip from '../../../components/controls/Tooltip';
+import { translate, translateWithParameters } from '../../../helpers/l10n';
+import { RuleStatus } from '../../../types/rules';
+import DocumentationTooltip from '../../common/DocumentationTooltip';
+import SonarLintIcon from '../../icons/SonarLintIcon';
+import { WorkspaceContext } from '../../workspace/context';
+
+export interface IssueMessageTagsProps {
+ engine?: string;
+ quickFixAvailable?: boolean;
+ ruleStatus?: RuleStatus;
+}
+
+export default function IssueMessageTags(props: IssueMessageTagsProps) {
+ const { engine, quickFixAvailable, ruleStatus } = props;
+
+ const { externalRulesRepoNames } = React.useContext(WorkspaceContext);
+ const ruleEngine = (engine && externalRulesRepoNames && externalRulesRepoNames[engine]) || engine;
+
+ return (
+ <>
+ {quickFixAvailable && (
+ <Tooltip
+ overlay={
+ <FormattedMessage
+ id="issue.quick_fix_available_with_sonarlint"
+ defaultMessage={translate('issue.quick_fix_available_with_sonarlint')}
+ values={{
+ link: (
+ <a
+ href="https://www.sonarqube.org/sonarlint/?referrer=sonarqube-quick-fix"
+ rel="noopener noreferrer"
+ target="_blank">
+ SonarLint
+ </a>
+ )
+ }}
+ />
+ }
+ mouseLeaveDelay={0.5}>
+ <SonarLintIcon
+ className="it__issues-sonarlint-quick-fix spacer-right"
+ size={15}
+ ariaLabel="sonar-lint-icon"
+ />
+ </Tooltip>
+ )}
+ {ruleStatus && (ruleStatus === RuleStatus.Deprecated || ruleStatus === RuleStatus.Removed) && (
+ <DocumentationTooltip
+ className="spacer-left"
+ content={translate('rules.status', ruleStatus, 'help')}
+ links={[
+ {
+ href: '/documentation/user-guide/rules/',
+ label: translateWithParameters('see_x', translate('rules'))
+ }
+ ]}>
+ <span className="spacer-right badge badge-error">
+ {translate('issue.resolution.badge', ruleStatus)}
+ </span>
+ </DocumentationTooltip>
+ )}
+ {ruleEngine && (
+ <Tooltip overlay={translateWithParameters('issue.from_external_rule_engine', ruleEngine)}>
+ <div className="badge spacer-right text-baseline">{ruleEngine}</div>
+ </Tooltip>
+ )}
+ </>
+ );
+}
diff --git a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueMessage-test.tsx.snap b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueMessage-test.tsx.snap
index eb1c297404f..3a8e73822e2 100644
--- a/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueMessage-test.tsx.snap
+++ b/server/sonar-web/src/main/js/components/issue/components/__tests__/__snapshots__/IssueMessage-test.tsx.snap
@@ -10,6 +10,7 @@ exports[`should render correctly: default 1`] = `
>
Reduce the number of conditional operators (4) used in the expression
</span>
+ <IssueMessageTags />
</div>
<ButtonLink
aria-label="issue.why_this_issue.long"
@@ -31,6 +32,7 @@ exports[`should render correctly: hide why is it an issue 1`] = `
>
Reduce the number of conditional operators (4) used in the expression
</span>
+ <IssueMessageTags />
</div>
</Fragment>
`;
@@ -45,24 +47,9 @@ exports[`should render correctly: is deprecated rule 1`] = `
>
Reduce the number of conditional operators (4) used in the expression
</span>
- <DocumentationTooltip
- className="spacer-left"
- content="rules.status.DEPRECATED.help"
- links={
- Array [
- Object {
- "href": "/documentation/user-guide/rules/",
- "label": "see_x.rules",
- },
- ]
- }
- >
- <span
- className="spacer-right badge badge-error"
- >
- issue.resolution.badge.DEPRECATED
- </span>
- </DocumentationTooltip>
+ <IssueMessageTags
+ ruleStatus="DEPRECATED"
+ />
</div>
<ButtonLink
aria-label="issue.why_this_issue.long"
@@ -84,24 +71,9 @@ exports[`should render correctly: is removed rule 1`] = `
>
Reduce the number of conditional operators (4) used in the expression
</span>
- <DocumentationTooltip
- className="spacer-left"
- content="rules.status.REMOVED.help"
- links={
- Array [
- Object {
- "href": "/documentation/user-guide/rules/",
- "label": "see_x.rules",
- },
- ]
- }
- >
- <span
- className="spacer-right badge badge-error"
- >
- issue.resolution.badge.REMOVED
- </span>
- </DocumentationTooltip>
+ <IssueMessageTags
+ ruleStatus="REMOVED"
+ />
</div>
<ButtonLink
aria-label="issue.why_this_issue.long"
@@ -123,15 +95,9 @@ exports[`should render correctly: with engine info 1`] = `
>
Reduce the number of conditional operators (4) used in the expression
</span>
- <Tooltip
- overlay="issue.from_external_rule_engine.js"
- >
- <div
- className="badge spacer-right text-baseline"
- >
- js
- </div>
- </Tooltip>
+ <IssueMessageTags
+ engine="js"
+ />
</div>
<ButtonLink
aria-label="issue.why_this_issue.long"
@@ -153,31 +119,9 @@ exports[`should render correctly: with quick fix 1`] = `
>
Reduce the number of conditional operators (4) used in the expression
</span>
- <Tooltip
- mouseLeaveDelay={0.5}
- overlay={
- <FormattedMessage
- defaultMessage="issue.quick_fix_available_with_sonarlint"
- id="issue.quick_fix_available_with_sonarlint"
- values={
- Object {
- "link": <a
- href="https://www.sonarqube.org/sonarlint/?referrer=sonarqube-quick-fix"
- rel="noopener noreferrer"
- target="_blank"
- >
- SonarLint
- </a>,
- }
- }
- />
- }
- >
- <SonarLintIcon
- className="it__issues-sonarlint-quick-fix spacer-right"
- size={15}
- />
- </Tooltip>
+ <IssueMessageTags
+ quickFixAvailable={true}
+ />
</div>
<ButtonLink
aria-label="issue.why_this_issue.long"
diff --git a/server/sonar-web/src/main/js/types/issues.ts b/server/sonar-web/src/main/js/types/issues.ts
index 2e12d798318..2c106f1ba60 100644
--- a/server/sonar-web/src/main/js/types/issues.ts
+++ b/server/sonar-web/src/main/js/types/issues.ts
@@ -64,6 +64,8 @@ export interface RawIssue {
textRange?: TextRange;
type: IssueType;
ruleDescriptionContextKey?: string;
+ ruleStatus?: string;
+ quickFixAvailable?: boolean;
}
export interface IssueResponse {