From 7428d9fb9766864a5a0821a901fdbe8879a8eb7b Mon Sep 17 00:00:00 2001 From: Kevin Silva Date: Tue, 3 Oct 2023 16:29:04 +0200 Subject: [PATCH] SONAR-20500 Rules details header --- .../components/RuleDetailsMeta.tsx | 253 ++++++++++-------- .../js/apps/issues/components/IssueHeader.tsx | 1 + .../src/main/js/components/tags/TagsList.tsx | 6 +- server/sonar-web/src/main/js/helpers/urls.ts | 2 +- 4 files changed, 153 insertions(+), 109 deletions(-) diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsMeta.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsMeta.tsx index a3345f1ff20..fcfd7c355c7 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsMeta.tsx +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsMeta.tsx @@ -17,22 +17,33 @@ * 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 { + Badge, + BasicSeparator, + ClipboardIconButton, + DisabledText, + HelperHintIcon, + IssueMessageHighlighting, + LightLabel, + Link, + LinkIcon, + Note, + PageContentFontWrapper, + themeBorder, +} from 'design-system'; import * as React from 'react'; -import { colors } from '../../../app/theme'; import DocumentationTooltip from '../../../components/common/DocumentationTooltip'; -import Link from '../../../components/common/Link'; import HelpTooltip from '../../../components/controls/HelpTooltip'; import Tooltip from '../../../components/controls/Tooltip'; -import IssueTypeIcon from '../../../components/icons/IssueTypeIcon'; -import LinkIcon from '../../../components/icons/LinkIcon'; -import DateFormatter from '../../../components/intl/DateFormatter'; +import IssueSeverityIcon from '../../../components/icon-mappers/IssueSeverityIcon'; +import IssueTypeIcon from '../../../components/icon-mappers/IssueTypeIcon'; import { CleanCodeAttributePill } from '../../../components/shared/CleanCodeAttributePill'; -import SeverityHelper from '../../../components/shared/SeverityHelper'; import SoftwareImpactPill from '../../../components/shared/SoftwareImpactPill'; import TagsList from '../../../components/tags/TagsList'; import { translate, translateWithParameters } from '../../../helpers/l10n'; -import { getRuleUrl } from '../../../helpers/urls'; +import { getPathUrlAsString, getRuleUrl } from '../../../helpers/urls'; +import { IssueSeverity as IssueSeverityType } from '../../../types/issues'; import { Dict, RuleDetails } from '../../../types/types'; import RuleDetailsTagsPopup from './RuleDetailsTagsPopup'; @@ -49,7 +60,7 @@ export default class RuleDetailsMeta extends React.PureComponent { renderType = () => { const { ruleDetails } = this.props; return ( -
  • + @@ -64,15 +75,17 @@ export default class RuleDetailsMeta extends React.PureComponent { }, ]} > - - {translate('issue.type', ruleDetails.type)} + + + {translate('issue.type', ruleDetails.type)} + -
  • + ); }; renderSeverity = () => ( -
  • + @@ -87,13 +100,16 @@ export default class RuleDetailsMeta extends React.PureComponent { }, ]} > - + + + {translate('severity', this.props.ruleDetails.severity)} + -
  • + ); renderStatus = () => { @@ -103,9 +119,9 @@ export default class RuleDetailsMeta extends React.PureComponent { } return ( -
  • - {translate('rules.status', ruleDetails.status)} -
  • + + {translate('rules.status', ruleDetails.status)} +
    ); }; @@ -114,11 +130,13 @@ export default class RuleDetailsMeta extends React.PureComponent { const { canWrite, ruleDetails } = this.props; const { sysTags = [], tags = [] } = ruleDetails; const allTags = [...sysTags, ...tags]; + const TAGS_TO_DISPLAY = 1; return ( -
    +
    0 ? allTags : [translate('coding_rules.no_tags')]} overlay={ canWrite ? ( @@ -134,13 +152,6 @@ export default class RuleDetailsMeta extends React.PureComponent { ); }; - renderCreationDate = () => ( -
  • - {translate('coding_rules.available_since')} - -
  • - ); - renderRepository = () => { const { referencedRepositories, ruleDetails } = this.props; const repository = referencedRepositories[ruleDetails.repo]; @@ -149,9 +160,9 @@ export default class RuleDetailsMeta extends React.PureComponent { } return ( -
  • + {repository.name} ({ruleDetails.langName}) -
  • +
    ); }; @@ -162,7 +173,9 @@ export default class RuleDetailsMeta extends React.PureComponent { } return ( -
  • {translate('coding_rules.rule_template')}
  • + + {translate('coding_rules.rule_template')} +
    ); }; @@ -173,18 +186,17 @@ export default class RuleDetailsMeta extends React.PureComponent { return null; } return ( -
  • + {translate('coding_rules.custom_rule')} {' ('} {translate('coding_rules.show_template')} - {')'} - -
  • + {') '} + + + + ); }; @@ -194,15 +206,21 @@ export default class RuleDetailsMeta extends React.PureComponent { return null; } return ( - -
  • - {translate('coding_rules.remediation_function', ruleDetails.remFnType)} - {':'} - {ruleDetails.remFnBaseEffort !== undefined && ` ${ruleDetails.remFnBaseEffort}`} - {ruleDetails.remFnGapMultiplier !== undefined && ` +${ruleDetails.remFnGapMultiplier}`} - {ruleDetails.gapDescription !== undefined && ` ${ruleDetails.gapDescription}`} -
  • -
    + <> + + + + + {ruleDetails.remFnBaseEffort !== undefined && ` ${ruleDetails.remFnBaseEffort}`} + {ruleDetails.remFnGapMultiplier !== undefined && + ` +${ruleDetails.remFnGapMultiplier}`} + {ruleDetails.gapDescription !== undefined && ` ${ruleDetails.gapDescription}`} + + + + ); }; @@ -217,9 +235,9 @@ export default class RuleDetailsMeta extends React.PureComponent { } return ( -
  • -
    {engine}
    -
  • + + {engine} +
    ); }; @@ -230,79 +248,104 @@ export default class RuleDetailsMeta extends React.PureComponent { const displayedKey = ruleDetails.key.startsWith(EXTERNAL_PREFIX) ? ruleDetails.key.substring(EXTERNAL_PREFIX.length) : ruleDetails.key; - return {displayedKey}; + return {displayedKey}; } render() { const { ruleDetails } = this.props; + const ruleUrl = getRuleUrl(ruleDetails.key); + const hasTypeData = !ruleDetails.isExternal || ruleDetails.type !== 'UNKNOWN'; return ( -
    -
    - {ruleDetails.cleanCodeAttributeCategory !== undefined && ( - - )} -
    - {this.renderKey()} - {!ruleDetails.isExternal && ( - - - +
    +
    +
    + {ruleDetails.cleanCodeAttributeCategory !== undefined && ( + )}
    -
    - -
    -

    {ruleDetails.name}

    - {this.renderTags()} -
    -
    - {!!ruleDetails.impacts.length && ( -
    - {translate('issue.software_qualities.label')} -
      - {ruleDetails.impacts.map(({ severity, softwareQuality }) => ( -
    • - -
    • - ))} -
    -
    - )} +
    + + + + +
    +
    + {!!ruleDetails.impacts.length && ( +
    + {translate('issue.software_qualities.label')} +
      + {ruleDetails.impacts.map(({ severity, softwareQuality }) => ( +
    • + +
    • + ))} +
    +
    + )} +
    + {hasTypeData && ( -
      - {this.renderType()} - {this.renderSeverity()} - {!ruleDetails.isExternal && this.renderStatus()} - {!ruleDetails.isExternal && this.renderCreationDate()} - {this.renderRepository()} +
      {!ruleDetails.isExternal && ( <> {this.renderTemplate()} {this.renderParentTemplate()} - {this.renderRemediation()} )} + + {this.renderRepository()} + {this.renderType()} + {this.renderSeverity()} {ruleDetails.isExternal && this.renderExternalBadge()} -
    + {!ruleDetails.isExternal && this.renderStatus()} +
    )}
    -
    + + {this.renderKey()} + + + {this.renderTags()} + + {this.renderRemediation()} + + ); } } + +function RightMetaHeaderInfo({ + title, + children, +}: Readonly<{ title: string; children: React.ReactNode }>) { + return ( +
    + + {title} + + {children} +
    + ); +} + +const StyledSection = styled.div` + border-left: ${themeBorder('default', 'pageBlockBorder')}; +`; 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 2229f306e41..433ba768ea2 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 @@ -160,6 +160,7 @@ export default class IssueHeader extends React.PureComponent { open: issue.key, types: issue.type === IssueType.SecurityHotspot ? issue.type : undefined, }); + const canSetTags = issue.actions.includes(IssueActions.SetTags); return ( diff --git a/server/sonar-web/src/main/js/components/tags/TagsList.tsx b/server/sonar-web/src/main/js/components/tags/TagsList.tsx index 07318b147fd..f29a548ee95 100644 --- a/server/sonar-web/src/main/js/components/tags/TagsList.tsx +++ b/server/sonar-web/src/main/js/components/tags/TagsList.tsx @@ -28,15 +28,15 @@ interface Props { className?: string; tags: string[]; overlay?: React.ReactNode; + tagsToDisplay?: number; } -const TAGS_TO_DISPLAY = 2; - export default function TagsList({ allowUpdate = false, className, tags, overlay, + tagsToDisplay = 2, }: Readonly) { const [open, setOpen] = React.useState(false); @@ -52,7 +52,7 @@ export default function TagsList({ overlay={overlay} popupPlacement={PopupPlacement.Bottom} tags={tags} - tagsToDisplay={TAGS_TO_DISPLAY} + tagsToDisplay={tagsToDisplay} tooltip={Tooltip} /> ); diff --git a/server/sonar-web/src/main/js/helpers/urls.ts b/server/sonar-web/src/main/js/helpers/urls.ts index 4ccd67c7218..99b03c8a463 100644 --- a/server/sonar-web/src/main/js/helpers/urls.ts +++ b/server/sonar-web/src/main/js/helpers/urls.ts @@ -366,7 +366,7 @@ export function getProjectSettingsUrl(id: string, category?: string): Partial { return { pathname: '/coding_rules', search: queryToSearch(query) }; } -- 2.39.5