From: Ambroise C Date: Wed, 31 May 2023 15:36:03 +0000 (+0200) Subject: SONAR-19345 Update selected issue's header section to new design system X-Git-Tag: 10.1.0.73491~119 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=b6212a6f856a4aff6688fae41633b089cfe774e1;p=sonarqube.git SONAR-19345 Update selected issue's header section to new design system --- diff --git a/server/sonar-web/design-system/src/components/IssueMessageHighlighting.tsx b/server/sonar-web/design-system/src/components/IssueMessageHighlighting.tsx new file mode 100644 index 00000000000..f16621bd419 --- /dev/null +++ b/server/sonar-web/design-system/src/components/IssueMessageHighlighting.tsx @@ -0,0 +1,118 @@ +/* + * SonarQube + * Copyright (C) 2009-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 styled from '@emotion/styled'; +import * as React from 'react'; +import { themeColor } from '../helpers'; + +export interface MessageFormatting { + end: number; + start: number; + type: MessageFormattingType; +} + +export enum MessageFormattingType { + CODE = 'CODE', +} + +export interface IssueMessageHighlightingProps { + message?: string; + messageFormattings?: MessageFormatting[]; +} + +export function IssueMessageHighlighting(props: IssueMessageHighlightingProps) { + const { message, messageFormattings } = props; + + if (!message) { + return null; + } + + if (!(messageFormattings && messageFormattings.length > 0)) { + return <>{message}; + } + + let previousEnd = 0; + + const sanitizedFormattings = [...messageFormattings] + .sort((a, b) => a.start - b.start) + .reduce((acc, messageFormatting) => { + const { type } = messageFormatting; + + if (type !== MessageFormattingType.CODE) { + return acc; + } + + const { start } = messageFormatting; + let { end } = messageFormatting; + + end = Math.min(message.length, end); + + if (start < 0 || end === start || end < start) { + return acc; + } + + if (acc.length > 0) { + const { start: previousStart, end: previousEnd } = acc[acc.length - 1]; + + if (start <= previousEnd) { + acc[acc.length - 1] = { + start: previousStart, + end: Math.max(previousEnd, end), + type, + }; + + return acc; + } + } + + acc.push({ start, end, type }); + + return acc; + }, []); + + return ( + + {sanitizedFormattings.map(({ start, end, type }) => { + const beginning = previousEnd; + previousEnd = end; + + return ( + + {message.slice(beginning, start)} + {type === MessageFormattingType.CODE ? ( + + {message.slice(start, end)} + + ) : ( + {message.slice(start, end)} + )} + + ); + })} + + {message.slice(previousEnd)} + + ); +} + +const SingleLineSnippet = styled.span` + background: ${themeColor('codeSnippetBackground')}; + border-color: ${themeColor('codeSnippetBorder')}; + color: ${themeColor('codeSnippetInline')}; +`; diff --git a/server/sonar-web/design-system/src/components/__tests__/IssueMessageHighlighting-test.tsx b/server/sonar-web/design-system/src/components/__tests__/IssueMessageHighlighting-test.tsx new file mode 100644 index 00000000000..70d2cdf3214 --- /dev/null +++ b/server/sonar-web/design-system/src/components/__tests__/IssueMessageHighlighting-test.tsx @@ -0,0 +1,73 @@ +/* + * SonarQube + * Copyright (C) 2009-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 { render } from '@testing-library/react'; +import { + IssueMessageHighlighting, + IssueMessageHighlightingProps, + MessageFormattingType, +} from '../IssueMessageHighlighting'; + +it.each([ + [undefined, undefined], + ['message', undefined], + ['message', []], + ['message', [{ start: 1, end: 4, type: 'something else' as MessageFormattingType }]], + [ + 'message', + [ + { start: 5, end: 6, type: MessageFormattingType.CODE }, + { start: 1, end: 4, type: MessageFormattingType.CODE }, + ], + ], + [ + 'a somewhat longer message with overlapping ranges', + [{ start: -1, end: 1, type: MessageFormattingType.CODE }], + ], + [ + 'a somewhat longer message with overlapping ranges', + [{ start: 48, end: 70, type: MessageFormattingType.CODE }], + ], + [ + 'a somewhat longer message with overlapping ranges', + [{ start: 0, end: 0, type: MessageFormattingType.CODE }], + ], + [ + 'a somewhat longer message with overlapping ranges', + [ + { start: 11, end: 17, type: MessageFormattingType.CODE }, + { start: 2, end: 25, type: MessageFormattingType.CODE }, + { start: 25, end: 2, type: MessageFormattingType.CODE }, + ], + ], + [ + 'a somewhat longer message with overlapping ranges', + [ + { start: 18, end: 30, type: MessageFormattingType.CODE }, + { start: 2, end: 25, type: MessageFormattingType.CODE }, + ], + ], +])('should format the string with highlights', (message, messageFormattings) => { + const { asFragment } = renderIssueMessageHighlighting({ message, messageFormattings }); + expect(asFragment()).toMatchSnapshot(); +}); + +function renderIssueMessageHighlighting(props: Partial = {}) { + return render(); +} diff --git a/server/sonar-web/design-system/src/components/__tests__/__snapshots__/IssueMessageHighlighting-test.tsx.snap b/server/sonar-web/design-system/src/components/__tests__/__snapshots__/IssueMessageHighlighting-test.tsx.snap new file mode 100644 index 00000000000..008c32eca0a --- /dev/null +++ b/server/sonar-web/design-system/src/components/__tests__/__snapshots__/IssueMessageHighlighting-test.tsx.snap @@ -0,0 +1,124 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should format the string with highlights 1`] = ``; + +exports[`should format the string with highlights 2`] = ` + + message + +`; + +exports[`should format the string with highlights 3`] = ` + + message + +`; + +exports[`should format the string with highlights 4`] = ` + + + message + + +`; + +exports[`should format the string with highlights 5`] = ` + + .emotion-0 { + background: rgb(252,252,253); + border-color: rgb(225,230,243); + color: rgb(62,67,87); +} + + + m + + ess + + a + + g + + e + + +`; + +exports[`should format the string with highlights 6`] = ` + + + a somewhat longer message with overlapping ranges + + +`; + +exports[`should format the string with highlights 7`] = ` + + .emotion-0 { + background: rgb(252,252,253); + border-color: rgb(225,230,243); + color: rgb(62,67,87); +} + + + a somewhat longer message with overlapping range + + s + + + +`; + +exports[`should format the string with highlights 8`] = ` + + + a somewhat longer message with overlapping ranges + + +`; + +exports[`should format the string with highlights 9`] = ` + + .emotion-0 { + background: rgb(252,252,253); + border-color: rgb(225,230,243); + color: rgb(62,67,87); +} + + + a + + somewhat longer message + + with overlapping ranges + + +`; + +exports[`should format the string with highlights 10`] = ` + + .emotion-0 { + background: rgb(252,252,253); + border-color: rgb(225,230,243); + color: rgb(62,67,87); +} + + + a + + somewhat longer message with + + overlapping ranges + + +`; diff --git a/server/sonar-web/design-system/src/components/index.ts b/server/sonar-web/design-system/src/components/index.ts index 5457b363900..2724f1199d8 100644 --- a/server/sonar-web/design-system/src/components/index.ts +++ b/server/sonar-web/design-system/src/components/index.ts @@ -54,6 +54,7 @@ export * from './InputMultiSelect'; export { InputSearch } from './InputSearch'; export * from './InputSelect'; export * from './InteractiveIcon'; +export * from './IssueMessageHighlighting'; export * from './KeyboardHint'; export * from './Link'; export { StandoutLink as Link } from './Link'; diff --git a/server/sonar-web/src/main/js/apps/issues/__tests__/IssuesApp-it.tsx b/server/sonar-web/src/main/js/apps/issues/__tests__/IssuesApp-it.tsx index 8bcf538a86b..336135bf760 100644 --- a/server/sonar-web/src/main/js/apps/issues/__tests__/IssuesApp-it.tsx +++ b/server/sonar-web/src/main/js/apps/issues/__tests__/IssuesApp-it.tsx @@ -24,6 +24,7 @@ import selectEvent from 'react-select-event'; import { TabKeys } from '../../../components/rules/RuleTabViewer'; import { renderOwaspTop102021Category } from '../../../helpers/security-standard'; import { mockLoggedInUser, mockRawIssue } from '../../../helpers/testMocks'; +import { findTooltipWithContent } from '../../../helpers/testReactTestingUtils'; import { ComponentQualifier } from '../../../types/component'; import { IssueType } from '../../../types/issues'; import { @@ -775,17 +776,16 @@ describe('issues item', () => { // open tags popup on key press 't' - // needs to be fixed with the new header from ambroise! - // await user.keyboard('t'); - // expect(screen.getByRole('searchbox', { name: 'search.search_for_tags' })).toBeInTheDocument(); - // expect(screen.getByText('android')).toBeInTheDocument(); - // expect(screen.getByText('accessibility')).toBeInTheDocument(); - // // closing tags popup - // await user.click(screen.getByText('issue.no_tag')); - - // // open assign popup on key press 'a' - // await user.keyboard('a'); - // expect(screen.getByRole('searchbox', { name: 'search.search_for_tags' })).toBeInTheDocument(); + await user.keyboard('t'); + expect(screen.getByRole('searchbox', { name: 'search.search_for_tags' })).toBeInTheDocument(); + expect(screen.getByText('android')).toBeInTheDocument(); + expect(screen.getByText('accessibility')).toBeInTheDocument(); + // closing tags popup + await user.click(screen.getByText('issue.no_tag')); + + // open assign popup on key press 'a' + await user.keyboard('a'); + expect(screen.getByRole('searchbox', { name: 'search.search_for_tags' })).toBeInTheDocument(); }); it('should not open the actions popup using keyboard shortcut when keyboard shortcut flag is disabled', async () => { @@ -864,8 +864,11 @@ describe('issues item', () => { await user.click(await ui.issueItemAction7.find()); expect( - screen.getByRole('heading', { - name: 'Issue with tags issue.quick_fix_available_with_sonarlint_no_link issue.resolution.badge.DEPRECATED', + findTooltipWithContent('issue.quick_fix_available_with_sonarlint_no_link') + ).toBeInTheDocument(); + expect( + screen.getByRole('status', { + name: 'issue.resolution.badge.DEPRECATED', }) ).toBeInTheDocument(); }); 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 1ee28169b96..91a9824f9ac 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 @@ -17,24 +17,27 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { + ClipboardIconButton, + IssueMessageHighlighting, + Link, + LinkIcon, + Note, + PageContentFontWrapper, +} from 'design-system'; import * as React from 'react'; import { setIssueAssignee } from '../../../api/issues'; -import Link from '../../../components/common/Link'; -import LinkIcon from '../../../components/icons/LinkIcon'; -import { IssueMessageHighlighting } from '../../../components/issue/IssueMessageHighlighting'; 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 IssueTags from '../../../components/issue/components/IssueTags'; import { getBranchLikeQuery } from '../../../helpers/branch-like'; import { isInput, isShortcut } from '../../../helpers/keyboardEventHelpers'; import { KeyboardKeys } from '../../../helpers/keycodes'; import { translate } from '../../../helpers/l10n'; import { getKeyboardShortcutEnabled } from '../../../helpers/preferences'; -import { getComponentIssuesUrl, getRuleUrl } from '../../../helpers/urls'; +import { getComponentIssuesUrl, getPathUrlAsString, getRuleUrl } from '../../../helpers/urls'; import { BranchLike } from '../../../types/branch-like'; -import { IssueType } from '../../../types/issues'; -import { RuleStatus } from '../../../types/rules'; +import { IssueActions, IssueType } from '../../../types/issues'; import { Issue, RuleDetails } from '../../../types/types'; interface Props { @@ -131,65 +134,43 @@ export default class IssueHeader extends React.PureComponent { open: issue.key, types: issue.type === IssueType.SecurityHotspot ? issue.type : undefined, }); - const ruleStatus = issue.ruleStatus as RuleStatus | undefined; - const { quickFixAvailable } = issue; + const canSetTags = issue.actions.includes(IssueActions.SetTags); return ( - <> -
-

- - - - +
+ + -

-
- - {translate('issue.action.permalink')} - - -
+ +
-
-
- {name} +
+ + {name} {isExternal ? ( - ({key}) + ({key}) ) : ( - + {key} )} -
-
-
- -
- {issue.textRange != null && ( -
- - L{issue.textRange.endLine} - -
- )} -
+ +
{ togglePopup={this.handleIssuePopupToggle} showCommentsInPopup /> - + ); } } diff --git a/server/sonar-web/src/main/js/components/issue/__tests__/Issue-it.tsx b/server/sonar-web/src/main/js/components/issue/__tests__/Issue-it.tsx index 4be902198db..dc3166bab0f 100644 --- a/server/sonar-web/src/main/js/components/issue/__tests__/Issue-it.tsx +++ b/server/sonar-web/src/main/js/components/issue/__tests__/Issue-it.tsx @@ -154,21 +154,20 @@ describe('updating', () => { expect(ui.updateAssigneeBtn('luke').get()).toBeInTheDocument(); }); - // Should be re-enabled when tags are re-enabled with ambroise code // eslint-disable-next-line jest/no-commented-out-tests - // it('should allow updating the tags', async () => { - // const { ui } = getPageObject(); - // const issue = mockRawIssue(false, { - // tags: [], - // actions: [IssueActions.SetTags], - // }); - // issuesHandler.setIssueList([{ issue, snippets: {} }]); - // renderIssue({ issue: mockIssue(false, { ...pick(issue, 'actions', 'key', 'tags') }) }); - - // await ui.addTag('accessibility'); - // await ui.addTag('android', ['accessibility']); - // expect(ui.updateTagsBtn(['accessibility', 'android']).get()).toBeInTheDocument(); - // }); + it('should allow updating the tags', async () => { + const { ui } = getPageObject(); + const issue = mockRawIssue(false, { + tags: [], + actions: [IssueActions.SetTags], + }); + issuesHandler.setIssueList([{ issue, snippets: {} }]); + renderIssue({ issue: mockIssue(false, { ...pick(issue, 'actions', 'key', 'tags') }) }); + + await ui.addTag('accessibility'); + await ui.addTag('android', ['accessibility']); + expect(ui.updateTagsBtn(['accessibility', 'android']).get()).toBeInTheDocument(); + }); }); it('should correctly handle keyboard shortcuts', async () => { @@ -297,7 +296,7 @@ function getPageObject() { // Tags tagsSearchInput: byRole('searchbox'), updateTagsBtn: (currentTags?: string[]) => - byText(`tags_list_x.${currentTags ? currentTags.join(', ') : 'issue.no_tag'}`), + byRole('button', { name: `${currentTags ? currentTags.join(', ') : 'issue.no_tag'} +` }), toggleTagCheckbox: (name: string) => byRole('checkbox', { name }), }; diff --git a/server/sonar-web/src/main/js/components/issue/components/IssueActionsBar.tsx b/server/sonar-web/src/main/js/components/issue/components/IssueActionsBar.tsx index 28005887c65..4716deb8354 100644 --- a/server/sonar-web/src/main/js/components/issue/components/IssueActionsBar.tsx +++ b/server/sonar-web/src/main/js/components/issue/components/IssueActionsBar.tsx @@ -33,10 +33,11 @@ import { RuleStatus } from '../../../types/rules'; import { Issue, RawQuery } from '../../../types/types'; import Tooltip from '../../controls/Tooltip'; import DateFromNow from '../../intl/DateFromNow'; +import { WorkspaceContext } from '../../workspace/context'; import { updateIssue } from '../actions'; import IssueAssign from './IssueAssign'; +import IssueBadges from './IssueBadges'; import IssueCommentAction from './IssueCommentAction'; -import IssueMessageTags from './IssueMessageTags'; import IssueSeverity from './IssueSeverity'; import IssueTransition from './IssueTransition'; import IssueType from './IssueType'; @@ -58,138 +59,138 @@ interface State { commentPlaceholder: string; } -export default class IssueActionsBar extends React.PureComponent { - state: State = { +export default function IssueActionsBar(props: Props) { + const { + issue, + currentPopup, + onAssign, + onChange, + togglePopup, + className, + showComments, + showCommentsInPopup, + showLine, + } = props; + const [commentState, setCommentState] = React.useState({ commentAutoTriggered: false, commentPlaceholder: '', - }; + }); - setIssueProperty = ( + const setIssueProperty = ( property: keyof Issue, popup: string, apiCall: (query: RawQuery) => Promise, value: string ) => { - const { issue } = this.props; if (issue[property] !== value) { const newIssue = { ...issue, [property]: value }; - updateIssue( - this.props.onChange, - apiCall({ issue: issue.key, [property]: value }), - issue, - newIssue - ); + updateIssue(onChange, apiCall({ issue: issue.key, [property]: value }), issue, newIssue); } - this.props.togglePopup(popup, false); + togglePopup(popup, false); }; - toggleComment = (open: boolean | undefined, placeholder = '', autoTriggered = false) => { - this.setState({ + const toggleComment = (open: boolean | undefined, placeholder = '', autoTriggered = false) => { + setCommentState({ commentPlaceholder: placeholder, commentAutoTriggered: autoTriggered, }); - this.props.togglePopup('comment', open); + togglePopup('comment', open); }; - handleTransition = (issue: Issue) => { - this.props.onChange(issue); + const handleTransition = (issue: Issue) => { + onChange(issue); if ( issue.resolution === IssueResolution.FalsePositive || (issue.resolution === IssueResolution.WontFix && issue.type !== IssueTypeEnum.SecurityHotspot) ) { - this.toggleComment(true, translate('issue.comment.explain_why'), true); + toggleComment(true, translate('issue.comment.explain_why'), true); } }; - render() { - const { issue, className, showComments, showCommentsInPopup, showLine } = this.props; - const canAssign = issue.actions.includes(IssueActions.Assign); - const canComment = issue.actions.includes(IssueActions.Comment); - const canSetSeverity = issue.actions.includes(IssueActions.SetSeverity); - const canSetType = issue.actions.includes(IssueActions.SetType); - const hasTransitions = issue.transitions.length > 0; - const hasComments = issue.comments && issue.comments.length > 0; - const IssueMetaLiClass = classNames( - className, - 'sw-body-sm sw-overflow-hidden sw-whitespace-nowrap sw-max-w-abs-150' - ); + const { externalRulesRepoNames } = React.useContext(WorkspaceContext); + const ruleEngine = + (issue.externalRuleEngine && externalRulesRepoNames[issue.externalRuleEngine]) || + issue.externalRuleEngine; + const canAssign = issue.actions.includes(IssueActions.Assign); + const canComment = issue.actions.includes(IssueActions.Comment); + const canSetSeverity = issue.actions.includes(IssueActions.SetSeverity); + const canSetType = issue.actions.includes(IssueActions.SetType); + const hasTransitions = issue.transitions.length > 0; + const hasComments = !!issue.comments?.length; + const issueMetaListItemClassNames = classNames( + className, + 'sw-body-sm sw-overflow-hidden sw-whitespace-nowrap sw-max-w-abs-150' + ); - return ( -
-
    -
  • - -
  • -
  • - -
  • -
  • - -
  • -
  • - -
  • -
- {(canComment || showCommentsInPopup) && ( - +
    +
  • + +
  • +
  • + - )} +
  • +
  • + +
  • +
  • + +
  • +
+ {(canComment || showCommentsInPopup) && ( + + )} -
    -
  • - -
  • +
      +
    • + +
    • - {issue.externalRuleEngine && ( -
    • - - {issue.externalRuleEngine} - -
    • - )} + {ruleEngine && ( +
    • + + {ruleEngine} + +
    • + )} - {issue.codeVariants && issue.codeVariants.length > 0 && ( - + {!!issue.codeVariants?.length && ( + <> + <> {issue.codeVariants.length > 1 @@ -197,46 +198,46 @@ export default class IssueActionsBar extends React.PureComponent { : translate('issue.1_code_variant')} - - - )} + + + + )} - {showComments && hasComments && ( - <> - - - {issue.comments?.length} - - - - )} - {showLine && isDefined(issue.textRange) && ( - <> - - - {translateWithParameters('issue.ncloc_x.short', issue.textRange.endLine)} - - - - - )} - {issue.effort && ( - <> - - {translateWithParameters('issue.x_effort', issue.effort)} - - - - )} - - - -
    -
- ); - } + {showComments && hasComments && ( + <> + + + {issue.comments?.length} + + + + )} + {showLine && isDefined(issue.textRange) && ( + <> + + + {translateWithParameters('issue.ncloc_x.short', issue.textRange.endLine)} + + + + + )} + {issue.effort && ( + <> + + {translateWithParameters('issue.x_effort', issue.effort)} + + + + )} + + + + +
+ ); } -const IssueMetaLi = styled.li` +const IssueMetaListItem = styled.li` color: ${themeColor('pageContentLight')}; `; diff --git a/server/sonar-web/src/main/js/components/issue/components/IssueBadges.tsx b/server/sonar-web/src/main/js/components/issue/components/IssueBadges.tsx new file mode 100644 index 00000000000..6fc6ff465d1 --- /dev/null +++ b/server/sonar-web/src/main/js/components/issue/components/IssueBadges.tsx @@ -0,0 +1,86 @@ +/* + * SonarQube + * Copyright (C) 2009-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 { Badge } from 'design-system'; +import * as React from 'react'; +import { FormattedMessage } from 'react-intl'; +import { translate, translateWithParameters } from '../../../helpers/l10n'; +import { RuleStatus } from '../../../types/rules'; +import DocumentationTooltip from '../../common/DocumentationTooltip'; +import Link from '../../common/Link'; +import Tooltip from '../../controls/Tooltip'; +import SonarLintIcon from '../../icons/SonarLintIcon'; + +export interface IssueBadgesProps { + quickFixAvailable?: boolean; + ruleStatus?: RuleStatus; +} + +export default function IssueBadges(props: IssueBadgesProps) { + const { quickFixAvailable, ruleStatus } = props; + + return ( +
+ {quickFixAvailable && ( + + SonarLint + + ), + }} + /> + } + mouseLeaveDelay={0.5} + > +
+ +
+
+ )} + {ruleStatus && + (ruleStatus === RuleStatus.Deprecated || ruleStatus === RuleStatus.Removed) && ( + + {translate('issue.resolution.badge', ruleStatus)} + + )} +
+ ); +} 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 deleted file mode 100644 index 5bf6636996f..00000000000 --- a/server/sonar-web/src/main/js/components/issue/components/IssueMessageTags.tsx +++ /dev/null @@ -1,95 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-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 * 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 Link from '../../common/Link'; -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[engine]) || engine; - - return ( - <> - {quickFixAvailable && ( - - SonarLint - - ), - }} - /> - } - mouseLeaveDelay={0.5} - > - - - )} - {ruleStatus && - (ruleStatus === RuleStatus.Deprecated || ruleStatus === RuleStatus.Removed) && ( - - - {translate('issue.resolution.badge', ruleStatus)} - - - )} - {ruleEngine && ( - -
{ruleEngine}
-
- )} - - ); -}