<div className="sw-flex sw-items-center">
{!!ruleDetails.impacts.length && (
<div className="sw-flex sw-items-center sw-flex-1">
- <Note>{translate('issue.software_qualities.label')}</Note>
+ <Note>{translate('coding_rules.software_qualities.label')}</Note>
<SoftwareImpactPillList
className="sw-ml-1"
softwareImpacts={ruleDetails.impacts}
import { setIssueAssignee } from '../../../api/issues';
import { updateIssue } from '../../../components/issue/actions';
import IssueActionsBar from '../../../components/issue/components/IssueActionsBar';
-import { CleanCodeAttributePill } from '../../../components/shared/CleanCodeAttributePill';
-import SoftwareImpactPillList from '../../../components/shared/SoftwareImpactPillList';
import { WorkspaceContext } from '../../../components/workspace/context';
import { getBranchLikeQuery } from '../../../helpers/branch-like';
import { isInput, isShortcut } from '../../../helpers/keyboardEventHelpers';
import { IssueActions, IssueType } from '../../../types/issues';
import { Issue, RuleDetails } from '../../../types/types';
import IssueHeaderMeta from './IssueHeaderMeta';
+import IssueHeaderSide from './IssueHeaderSide';
import IssueNewStatusAndTransitionGuide from './IssueNewStatusAndTransitionGuide';
interface Props {
return (
<header className="sw-flex sw-mb-6">
- <div className="sw-mr-8 sw-flex-1">
- <CleanCodeAttributePill
- cleanCodeAttributeCategory={issue.cleanCodeAttributeCategory}
- cleanCodeAttribute={issue.cleanCodeAttribute}
- />
+ <div className="sw-mr-8 sw-flex-1 sw-flex sw-flex-col sw-gap-4">
+ <div className="sw-flex sw-flex-col sw-gap-2">
+ <div className="sw-flex sw-items-center">
+ <PageContentFontWrapper className="sw-body-md-highlight" as="h1">
+ <IssueMessageHighlighting
+ message={issue.message}
+ messageFormattings={issue.messageFormattings}
+ />
+ <ClipboardIconButton
+ Icon={LinkIcon}
+ aria-label={translate('permalink')}
+ className="sw-ml-1 sw-align-bottom"
+ copyValue={getPathUrlAsString(issueUrl, false)}
+ discreet
+ />
+ </PageContentFontWrapper>
+ </div>
- <div className="sw-flex sw-items-center sw-my-2">
- <PageContentFontWrapper className="sw-body-md-highlight" as="h1">
- <IssueMessageHighlighting
- message={issue.message}
- messageFormattings={issue.messageFormattings}
- />
- <ClipboardIconButton
- Icon={LinkIcon}
- aria-label={translate('permalink')}
- className="sw-ml-1 sw-align-bottom"
- copyValue={getPathUrlAsString(issueUrl, false)}
- discreet
- />
- </PageContentFontWrapper>
- </div>
- <div className="sw-flex sw-items-center sw-justify-between sw-mb-4">
- {this.renderRuleDescription()}
- </div>
- <div className="sw-flex sw-items-center">
- <Note>{translate('issue.software_qualities.label')}</Note>
- <div data-guiding-id="issue-2">
- <SoftwareImpactPillList className="sw-ml-1" softwareImpacts={issue.impacts} />
+ <div className="sw-flex sw-items-center sw-justify-between">
+ {this.renderRuleDescription()}
</div>
</div>
- <BasicSeparator className="sw-my-3" />
+
+ <IssueHeaderMeta issue={issue} />
+
+ <BasicSeparator />
+
<IssueActionsBar
currentPopup={issuePopupName}
issue={issue}
onAssign={this.handleAssignement}
onChange={this.props.onIssueChange}
togglePopup={this.handleIssuePopupToggle}
+ canSetTags={canSetTags}
+ showTags
showSonarLintBadge
/>
</div>
- <IssueHeaderMeta
- issue={issue}
- canSetTags={canSetTags}
- onIssueChange={this.props.onIssueChange}
- tagsPopupOpen={issuePopupName === 'edit-tags' && canSetTags}
- togglePopup={this.handleIssuePopupToggle}
- />
+ <IssueHeaderSide issue={issue} />
<IssueNewStatusAndTransitionGuide
run
issues={[issue]}
* 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 { BasicSeparator, LightLabel, themeBorder, Tooltip } from 'design-system';
+import { LightLabel, Note, SeparatorCircleIcon, Tooltip } from 'design-system';
import React from 'react';
import DateFromNow from '../../../components/intl/DateFromNow';
-import IssueTags from '../../../components/issue/components/IssueTags';
+import IssueSeverity from '../../../components/issue/components/IssueSeverity';
+import IssueType from '../../../components/issue/components/IssueType';
import { translate } from '../../../helpers/l10n';
import { Issue } from '../../../types/types';
interface Props {
issue: Issue;
- canSetTags: boolean;
- onIssueChange: (issue: Issue) => void;
- tagsPopupOpen?: boolean;
- togglePopup: (popup: string, show?: boolean) => void;
}
-export default function IssueHeaderMeta(props: Props) {
- const { issue, canSetTags, tagsPopupOpen } = props;
-
- const separator = <BasicSeparator className="sw-my-2" />;
-
+export default function IssueHeaderMeta({ issue }: Readonly<Props>) {
return (
- <StyledSection className="sw-flex sw-flex-col sw-pl-4 sw-min-w-abs-150 sw-max-w-abs-250">
- <HotspotHeaderInfo title={translate('issue.tags')}>
- <IssueTags
- canSetTags={canSetTags}
- issue={issue}
- onChange={props.onIssueChange}
- open={tagsPopupOpen}
- togglePopup={props.togglePopup}
- tagsToDisplay={1}
- />
- </HotspotHeaderInfo>
- {separator}
-
+ <Note className="sw-flex sw-items-center sw-gap-2 sw-text-xs">
{!!issue.codeVariants?.length && (
<>
- <HotspotHeaderInfo title={translate('issue.code_variants')} className="sw-truncate">
- <Tooltip overlay={issue.codeVariants.join(', ')}>
- <span>{issue.codeVariants.join(', ')}</span>
+ <div className="sw-flex sw-gap-1">
+ <span>{translate('issue.code_variants')}</span>
+ <Tooltip overlay={issue.codeVariants?.join(', ')}>
+ <span className="sw-font-semibold">
+ <LightLabel>{issue.codeVariants?.join(', ')}</LightLabel>
+ </span>
</Tooltip>
- </HotspotHeaderInfo>
- {separator}
+ </div>
+ <SeparatorCircleIcon />
</>
)}
- {issue.effort && (
+ {typeof issue.line === 'number' && (
<>
- <HotspotHeaderInfo title={translate('issue.effort')}>{issue.effort}</HotspotHeaderInfo>
- {separator}
+ <div className="sw-flex sw-gap-1">
+ <span>{translate('issue.line_affected')}</span>
+ <span className="sw-font-semibold">L{issue.line}</span>
+ </div>
+ <SeparatorCircleIcon />
</>
)}
- <HotspotHeaderInfo title={translate('issue.introduced')}>
- <DateFromNow date={issue.creationDate} />
- </HotspotHeaderInfo>
- </StyledSection>
- );
-}
+ {issue.effort && (
+ <>
+ <div className="sw-flex sw-gap-1">
+ <span>{translate('issue.effort')}</span>
+ <span className="sw-font-semibold">{issue.effort}</span>
+ </div>
+ <SeparatorCircleIcon />
+ </>
+ )}
-interface IssueHeaderMetaItemProps {
- children: React.ReactNode;
- title: string;
- className?: string;
-}
+ <div className="sw-flex sw-gap-1">
+ <span>{translate('issue.introduced')}</span>
+ <span className="sw-font-semibold">
+ <LightLabel>
+ <DateFromNow date={issue.creationDate} />
+ </LightLabel>
+ </span>
+ </div>
+ <SeparatorCircleIcon />
-function HotspotHeaderInfo({ children, title, className }: IssueHeaderMetaItemProps) {
- return (
- <div className={className}>
- <LightLabel as="div" className="sw-body-sm-highlight">
- {title}
- </LightLabel>
- {children}
- </div>
+ <IssueType issue={issue} />
+ <SeparatorCircleIcon data-guiding-id="issue-4" />
+ <IssueSeverity issue={issue} />
+ </Note>
);
}
-
-const StyledSection = styled.div`
- border-left: ${themeBorder('default', 'pageBlockBorder')};
-`;
--- /dev/null
+/*
+ * 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 { LightLabel, themeBorder } from 'design-system';
+import React from 'react';
+import { CleanCodeAttributePill } from '../../../components/shared/CleanCodeAttributePill';
+import SoftwareImpactPillList from '../../../components/shared/SoftwareImpactPillList';
+import { translate } from '../../../helpers/l10n';
+import { Issue } from '../../../types/types';
+
+interface Props {
+ issue: Issue;
+}
+
+export default function IssueHeaderSide({ issue }: Readonly<Props>) {
+ return (
+ <StyledSection className="sw-flex sw-flex-col sw-pl-4 sw-w-[200px]">
+ <IssueHeaderInfo title={translate('issue.cct_attribute.label')} className="sw-mb-6">
+ <CleanCodeAttributePill
+ cleanCodeAttributeCategory={issue.cleanCodeAttributeCategory}
+ cleanCodeAttribute={issue.cleanCodeAttribute}
+ />
+ </IssueHeaderInfo>
+
+ <IssueHeaderInfo
+ data-guiding-id="issue-2"
+ title={translate('issue.software_qualities.label')}
+ >
+ <SoftwareImpactPillList className="sw-flex-wrap" softwareImpacts={issue.impacts} />
+ </IssueHeaderInfo>
+ </StyledSection>
+ );
+}
+
+interface IssueHeaderMetaItemProps extends React.HTMLAttributes<HTMLDivElement> {
+ children: React.ReactNode;
+ title: string;
+ className?: string;
+}
+
+function IssueHeaderInfo({
+ children,
+ title,
+ className,
+ ...props
+}: Readonly<IssueHeaderMetaItemProps>) {
+ return (
+ <div className={className} {...props}>
+ <LightLabel as="div" className="sw-text-xs sw-font-semibold sw-mb-1">
+ {title}
+ </LightLabel>
+ {children}
+ </div>
+ );
+}
+
+const StyledSection = styled.div`
+ border-left: ${themeBorder('default', 'pageBlockBorder')};
+`;
import * as React from 'react';
import { IssueActions } from '../../../types/issues';
import { Issue } from '../../../types/types';
-import SoftwareImpactPillList from '../../shared/SoftwareImpactPillList';
import IssueAssign from './IssueAssign';
import { SonarLintBadge } from './IssueBadges';
import IssueCommentAction from './IssueCommentAction';
-import IssueSeverity from './IssueSeverity';
+import IssueTags from './IssueTags';
import IssueTransition from './IssueTransition';
-import IssueType from './IssueType';
interface Props {
issue: Issue;
onAssign: (login: string) => void;
onChange: (issue: Issue) => void;
togglePopup: (popup: string, show?: boolean) => void;
- showIssueImpact?: boolean;
showSonarLintBadge?: boolean;
+ showTags?: boolean;
+ canSetTags?: boolean;
}
-export default function IssueActionsBar(props: Props) {
+export default function IssueActionsBar(props: Readonly<Props>) {
const {
issue,
currentPopup,
onAssign,
onChange,
togglePopup,
- showIssueImpact,
showSonarLintBadge,
+ showTags,
+ canSetTags,
} = props;
const [commentPlaceholder, setCommentPlaceholder] = React.useState('');
const canAssign = issue.actions.includes(IssueActions.Assign);
const canComment = issue.actions.includes(IssueActions.Comment);
+ const tagsPopupOpen = currentPopup === 'edit-tags' && canSetTags;
return (
<div className="sw-flex sw-gap-3">
/>
</li>
- {showIssueImpact && (
- <li data-guiding-id="issue-2">
- <SoftwareImpactPillList className="sw-gap-3" softwareImpacts={issue.impacts} />
+ {showTags && (
+ <li>
+ <IssueTags
+ canSetTags={canSetTags}
+ issue={issue}
+ onChange={props.onChange}
+ open={tagsPopupOpen}
+ togglePopup={props.togglePopup}
+ tagsToDisplay={1}
+ />
</li>
)}
</li>
)}
</ul>
- <ul className="sw-flex sw-items-center sw-gap-3 sw-body-sm" data-guiding-id="issue-4">
- <li>
- <IssueType issue={issue} />
- </li>
-
- <li>
- <IssueSeverity issue={issue} />
- </li>
- </ul>
{canComment && (
<IssueCommentAction
issue: Pick<Issue, 'severity'>;
}
-export default function IssueSeverity({ issue }: Props) {
+export default function IssueSeverity({ issue }: Readonly<Props>) {
return (
<DocumentationTooltip
content={<DeprecatedFieldTooltip field="severity" />}
},
]}
>
- <TextSubdued className="sw-flex sw-items-center sw-gap-1">
+ <TextSubdued className="sw-flex sw-items-center sw-gap-1/2">
<IssueSeverityIcon
fill="iconSeverityDisabled"
severity={issue.severity as IssueSeverityType}
import IssueTagsPopup from '../popups/IssueTagsPopup';
interface Props extends ComponentContextShape {
- canSetTags: boolean;
+ canSetTags?: boolean;
issue: Pick<Issue, 'key' | 'tags'>;
onChange: (issue: Issue) => void;
open?: boolean;
issue: Pick<Issue, 'type'>;
}
-export default function IssueType({ issue }: Props) {
+export default function IssueType({ issue }: Readonly<Props>) {
return (
<DocumentationTooltip
content={<DeprecatedFieldTooltip field="type" />}
},
]}
>
- <TextSubdued className="sw-flex sw-items-center sw-gap-1">
+ <TextSubdued className="sw-flex sw-items-center sw-gap-1/2">
<IssueTypeIcon fill="iconTypeDisabled" type={issue.type} aria-hidden />
{translate('issue.type', issue.type)}
</TextSubdued>
onAssign={this.props.onAssign}
onChange={this.props.onChange}
togglePopup={this.props.togglePopup}
- showIssueImpact
/>
<IssueMetaBar issue={issue} />
</div>
issue.severity.deprecation.documentation=Documentation
issue.severity.new=The new severities
-issue.software_qualities.label=Software qualities impacted:
+issue.cct_attribute.label=Clean code attribute
+issue.software_qualities.label=Software qualities impacted
issue.impact.severity.tooltip=This issue has a {severity} impact on the {quality} of your software.
issue.clean_code_attribute_category.CONSISTENT=Consistency
issue.unresolved.description=Unresolved issues have not been addressed in any way.
issue.action.permalink=Get permalink
-issue.line_affected=Line affected
-issue.introduced=Introduced
-issue.code_variants=Code variant
+issue.line_affected=Line affected:
+issue.introduced=Introduced:
+issue.code_variants=Code variant:
issue.rule_status=Rule status
-issue.effort=Effort
+issue.effort=Effort:
issue.x_effort={0} effort
issue.ncloc_x.short=L{0}
issue.1_code_variant=1 variant
coding_rules._rules=rules
coding_rules.show_template=Show Template
coding_rules.skip_to_filters=Skip to rules filters
+coding_rules.software_qualities.label=Software qualities impacted:
coding_rules.to_select_rules=Select rules
coding_rules.to_navigate=Navigate to rule
coding_rules.type.deprecation.title=Types of detection rules are deprecated.