From 1963d4aebc783e6cd1a418ea0ec3b0c2eed6cc7b Mon Sep 17 00:00:00 2001 From: 7PH Date: Mon, 4 Dec 2023 17:38:42 +0100 Subject: [PATCH] SONAR-21169 Rework issues page item layout and styling --- .../design-system/src/components/Link.tsx | 4 + .../design-system/src/components/Tags.tsx | 10 ++- .../js/apps/issues/__tests__/IssueApp-it.tsx | 10 +-- .../apps/issues/__tests__/IssueHeader-it.tsx | 2 +- .../issues/components/IssueHeaderMeta.tsx | 30 +++---- .../issues/components/IssueHeaderSide.tsx | 2 +- .../components/project-card/ProjectCard.tsx | 1 + .../issue/components/IssueActionsBar.tsx | 4 +- .../issue/components/IssueBadges.tsx | 75 ----------------- .../issue/components/IssueMetaBar.tsx | 53 ++++++++---- .../components/issue/components/IssueTags.tsx | 3 +- .../issue/components/IssueTitleBar.tsx | 37 ++------ .../components/issue/components/IssueView.tsx | 30 +++++-- .../issue/components/SonarLintBadge.tsx | 84 +++++++++++++++++++ .../shared/SoftwareImpactPillList.tsx | 7 +- .../resources/org/sonar/l10n/core.properties | 3 +- 16 files changed, 198 insertions(+), 157 deletions(-) delete mode 100644 server/sonar-web/src/main/js/components/issue/components/IssueBadges.tsx create mode 100644 server/sonar-web/src/main/js/components/issue/components/SonarLintBadge.tsx diff --git a/server/sonar-web/design-system/src/components/Link.tsx b/server/sonar-web/design-system/src/components/Link.tsx index 47177ca0f54..241b0d3d9fb 100644 --- a/server/sonar-web/design-system/src/components/Link.tsx +++ b/server/sonar-web/design-system/src/components/Link.tsx @@ -180,6 +180,10 @@ export const HoverLink = styled(StyledBaseLink)` --active: ${themeColor('linkTooltipActive')}; --borderActive: ${themeBorder('default', 'linkBorder')}; } + + ${ExternalIcon} { + color: ${themeColor('linkDiscreet')}; + } `; HoverLink.displayName = 'HoverLink'; diff --git a/server/sonar-web/design-system/src/components/Tags.tsx b/server/sonar-web/design-system/src/components/Tags.tsx index 200415cfb65..fb8c71481e3 100644 --- a/server/sonar-web/design-system/src/components/Tags.tsx +++ b/server/sonar-web/design-system/src/components/Tags.tsx @@ -38,6 +38,7 @@ interface Props { overlay?: React.ReactNode; popupPlacement?: PopupPlacement; tags: string[]; + tagsClassName?: string; tagsToDisplay?: number; tooltip?: React.ComponentType>; } @@ -46,6 +47,7 @@ export function Tags({ allowUpdate = false, ariaTagsListLabel, className, + tagsClassName, emptyText, menuId = '', overlay, @@ -55,7 +57,7 @@ export function Tags({ tooltip, open, onClose, -}: Props) { +}: Readonly) { const displayedTags = tags.slice(0, tagsToDisplay); const extraTags = tags.slice(tagsToDisplay); const Tooltip = tooltip || React.Fragment; @@ -95,7 +97,10 @@ export function Tags({ > {({ a11yAttrs, onToggleClick, open }) => ( @@ -115,7 +120,6 @@ const TagLabel = styled.span` color: ${themeContrast('tag')}; background: ${themeColor('tag')}; - ${tw`sw-body-sm`} ${tw`sw-box-border`} ${tw`sw-truncate`} ${tw`sw-rounded-1/2`} 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 4fc43afb01c..3b94be4800e 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 @@ -392,16 +392,16 @@ describe('issue app', () => { ).toHaveAttribute('aria-current', 'true'); }); - it('should show issue tags if applicable', async () => { + it('should show sonarlint badge if applicable', async () => { const user = userEvent.setup(); issuesHandler.setIsAdmin(true); renderIssueApp(); - // Select an issue with an advanced rule + // Select an issue with quick fix available await user.click(await ui.issueItemAction7.find()); - await expect( - screen.getByText('issue.quick_fix_available_with_sonarlint_no_link'), - ).toHaveATooltipWithContent('issue.quick_fix_available_with_sonarlint'); + await expect(screen.getByText('issue.quick_fix')).toHaveATooltipWithContent( + 'issue.quick_fix_available_with_sonarlint', + ); }); }); diff --git a/server/sonar-web/src/main/js/apps/issues/__tests__/IssueHeader-it.tsx b/server/sonar-web/src/main/js/apps/issues/__tests__/IssueHeader-it.tsx index e15e23d3223..f97abb9a48d 100644 --- a/server/sonar-web/src/main/js/apps/issues/__tests__/IssueHeader-it.tsx +++ b/server/sonar-web/src/main/js/apps/issues/__tests__/IssueHeader-it.tsx @@ -84,7 +84,7 @@ it('renders correctly', async () => { expect(byText('issue.effort').get()).toBeInTheDocument(); // SonarLint badge - expect(byText('issue.quick_fix_available_with_sonarlint_no_link').get()).toBeInTheDocument(); + expect(byText('issue.quick_fix').get()).toBeInTheDocument(); // Rule external engine expect(byText('eslint').get()).toBeInTheDocument(); diff --git a/server/sonar-web/src/main/js/apps/issues/components/IssueHeaderMeta.tsx b/server/sonar-web/src/main/js/apps/issues/components/IssueHeaderMeta.tsx index 4feb23909d2..be6ea4d8239 100644 --- a/server/sonar-web/src/main/js/apps/issues/components/IssueHeaderMeta.tsx +++ b/server/sonar-web/src/main/js/apps/issues/components/IssueHeaderMeta.tsx @@ -31,21 +31,7 @@ interface Props { export default function IssueHeaderMeta({ issue }: Readonly) { return ( - - {!!issue.codeVariants?.length && ( - <> -
- {translate('issue.code_variants')} - - - {issue.codeVariants?.join(', ')} - - -
- - - )} - + {typeof issue.line === 'number' && ( <>
@@ -76,6 +62,20 @@ export default function IssueHeaderMeta({ issue }: Readonly) {
+ {!!issue.codeVariants?.length && ( + <> +
+ {translate('issue.code_variants')} + + + {issue.codeVariants?.join(', ')} + + +
+ + + )} + diff --git a/server/sonar-web/src/main/js/apps/issues/components/IssueHeaderSide.tsx b/server/sonar-web/src/main/js/apps/issues/components/IssueHeaderSide.tsx index 1617fd00eaa..8358c0c1cde 100644 --- a/server/sonar-web/src/main/js/apps/issues/components/IssueHeaderSide.tsx +++ b/server/sonar-web/src/main/js/apps/issues/components/IssueHeaderSide.tsx @@ -31,7 +31,7 @@ interface Props { export default function IssueHeaderSide({ issue }: Readonly) { return ( - + ) { {showSonarLintBadge && issue.quickFixAvailable && (
  • - +
  • )} 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 deleted file mode 100644 index fadbab92857..00000000000 --- a/server/sonar-web/src/main/js/components/issue/components/IssueBadges.tsx +++ /dev/null @@ -1,75 +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 { translate } from '../../../helpers/l10n'; -import Link from '../../common/Link'; -import Tooltip from '../../controls/Tooltip'; -import SonarLintIcon from '../../icons/SonarLintIcon'; - -export interface IssueBadgesProps { - quickFixAvailable?: boolean; -} - -export default function IssueBadges(props: IssueBadgesProps) { - const { quickFixAvailable } = props; - - return ( -
    - -
    - ); -} - -export function SonarLintBadge({ quickFixAvailable }: { quickFixAvailable?: boolean }) { - if (quickFixAvailable) { - return ( - - SonarLint - - ), - }} - /> - } - mouseLeaveDelay={0.5} - > -
    - -
    -
    - ); - } - - return null; -} diff --git a/server/sonar-web/src/main/js/components/issue/components/IssueMetaBar.tsx b/server/sonar-web/src/main/js/components/issue/components/IssueMetaBar.tsx index d25f0d6cf5d..c00d796976a 100644 --- a/server/sonar-web/src/main/js/components/issue/components/IssueMetaBar.tsx +++ b/server/sonar-web/src/main/js/components/issue/components/IssueMetaBar.tsx @@ -19,6 +19,7 @@ */ import styled from '@emotion/styled'; +import classNames from 'classnames'; import { Badge, CommentIcon, SeparatorCircleIcon, themeColor } from 'design-system'; import * as React from 'react'; import { translate, translateWithParameters } from '../../../helpers/l10n'; @@ -27,14 +28,16 @@ import { Issue } from '../../../types/types'; import Tooltip from '../../controls/Tooltip'; import DateFromNow from '../../intl/DateFromNow'; import { WorkspaceContext } from '../../workspace/context'; -import IssueBadges from './IssueBadges'; +import IssueSeverity from './IssueSeverity'; +import IssueType from './IssueType'; +import SonarLintBadge from './SonarLintBadge'; interface Props { issue: Issue; showLine?: boolean; } -export default function IssueMetaBar(props: Props) { +export default function IssueMetaBar(props: Readonly) { const { issue, showLine } = props; const { externalRulesRepoNames } = React.useContext(WorkspaceContext); @@ -46,22 +49,32 @@ export default function IssueMetaBar(props: Props) { const hasComments = !!issue.comments?.length; const issueMetaListItemClassNames = - 'sw-body-sm sw-overflow-hidden sw-whitespace-nowrap sw-max-w-abs-150'; + 'sw-body-xs sw-overflow-hidden sw-whitespace-nowrap sw-max-w-abs-150'; return ( -
      -
    • - -
    • +
        + {issue.quickFixAvailable && ( + <> +
      • + +
      • + + + )} {ruleEngine && ( -
      • - - - {ruleEngine} - - -
      • + <> +
      • + + + {ruleEngine} + + +
      • + + )} {!!issue.codeVariants?.length && ( @@ -81,7 +94,9 @@ export default function IssueMetaBar(props: Props) { {hasComments && ( <> - + {issue.comments?.length} @@ -115,6 +130,14 @@ export default function IssueMetaBar(props: Props) { + + + + + + + +
      ); } diff --git a/server/sonar-web/src/main/js/components/issue/components/IssueTags.tsx b/server/sonar-web/src/main/js/components/issue/components/IssueTags.tsx index fd5c14f1b8b..1e60ef1cfac 100644 --- a/server/sonar-web/src/main/js/components/issue/components/IssueTags.tsx +++ b/server/sonar-web/src/main/js/components/issue/components/IssueTags.tsx @@ -67,7 +67,8 @@ export class IssueTags extends React.PureComponent { void; - togglePopup: (popup: string, show?: boolean) => void; } -export default function IssueTitleBar(props: IssueTitleBarProps) { - const { issue, displayWhyIsThisAnIssue, currentPopup } = props; - const canSetTags = issue.actions.includes(IssueActions.SetTags); +export default function IssueTitleBar(props: Readonly) { + const { issue, displayWhyIsThisAnIssue, branchLike } = props; return ( -
      -
      - -
      - -
      -
      -
      - +
      +
      + +
      ); } diff --git a/server/sonar-web/src/main/js/components/issue/components/IssueView.tsx b/server/sonar-web/src/main/js/components/issue/components/IssueView.tsx index 7d7f6364ca4..7ad428f169f 100644 --- a/server/sonar-web/src/main/js/components/issue/components/IssueView.tsx +++ b/server/sonar-web/src/main/js/components/issue/components/IssueView.tsx @@ -20,15 +20,18 @@ import styled from '@emotion/styled'; import classNames from 'classnames'; -import { Checkbox, themeBorder } from 'design-system'; +import { BasicSeparator, Checkbox, themeBorder } from 'design-system'; import * as React from 'react'; import { deleteIssueComment, editIssueComment } from '../../../api/issues'; import { translate, translateWithParameters } from '../../../helpers/l10n'; import { BranchLike } from '../../../types/branch-like'; +import { IssueActions } from '../../../types/issues'; import { Issue } from '../../../types/types'; +import SoftwareImpactPillList from '../../shared/SoftwareImpactPillList'; import { updateIssue } from '../actions'; import IssueActionsBar from './IssueActionsBar'; import IssueMetaBar from './IssueMetaBar'; +import IssueTags from './IssueTags'; import IssueTitleBar from './IssueTitleBar'; interface Props { @@ -80,6 +83,7 @@ export default class IssueView extends React.PureComponent { const { issue, branchLike, checked, currentPopup, displayWhyIsThisAnIssue } = this.props; const hasCheckbox = this.props.onCheck != null; + const canSetTags = issue.actions.includes(IssueActions.SetTags); const issueClass = classNames('it__issue-item sw-p-3 sw-mb-4 sw-rounded-1 sw-bg-white', { selected: this.props.selected, @@ -93,9 +97,9 @@ export default class IssueView extends React.PureComponent { aria-label={issue.message} ref={(node) => (this.nodeRef = node)} > -
      +
      {hasCheckbox && ( - + { )} -
      +
      +
      + +
      + +
      +
      + + +
      ) { + return compact ? : ; +} + +function SonarLintBadgeFull() { + return ( + + + + {translate('issue.quick_fix')} + + + ); +} + +function SonarLintBadgeCompact() { + return ( + + SonarLint + + ), + }} + /> + } + mouseLeaveDelay={0.5} + > +
      + +
      +
      + ); +} diff --git a/server/sonar-web/src/main/js/components/shared/SoftwareImpactPillList.tsx b/server/sonar-web/src/main/js/components/shared/SoftwareImpactPillList.tsx index b7103e4faf5..de84d2d5437 100644 --- a/server/sonar-web/src/main/js/components/shared/SoftwareImpactPillList.tsx +++ b/server/sonar-web/src/main/js/components/shared/SoftwareImpactPillList.tsx @@ -28,10 +28,10 @@ interface SoftwareImpact { severity: SoftwareImpactSeverity; } -interface SoftwareImpactPillListProps - extends Pick[0], 'type'> { +interface SoftwareImpactPillListProps extends React.HTMLAttributes { softwareImpacts: Array; className?: string; + type?: Parameters[0]['type']; } const severityMap = { @@ -44,6 +44,7 @@ export default function SoftwareImpactPillList({ softwareImpacts, type, className, + ...props }: Readonly) { const getQualityLabel = (quality: SoftwareQuality) => translate('software_quality', quality); const sortingFn = (a: SoftwareImpact, b: SoftwareImpact) => { @@ -54,7 +55,7 @@ export default function SoftwareImpactPillList({ }; return ( -
        +
          {softwareImpacts .slice() .sort(sortingFn) diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index 0cf4fc63f0b..d47606dfb64 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -900,6 +900,7 @@ 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.quick_fix=Quick fix issue.quick_fix_available_with_sonarlint=Quick fix available in {link} issue.quick_fix_available_with_sonarlint_no_link=Quick fix available in SonarLint issue.comment.add_comment=Add Comment @@ -1082,7 +1083,7 @@ issue.unresolved.description=Unresolved issues have not been addressed in any wa issue.action.permalink=Get permalink issue.line_affected=Line affected: issue.introduced=Introduced: -issue.code_variants=Code variant: +issue.code_variants=Variants: issue.rule_status=Rule status issue.effort=Effort: issue.x_effort={0} effort -- 2.39.5