diff options
author | 7PH <benjamin.raymond@sonarsource.com> | 2024-01-30 19:13:25 +0100 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2024-01-31 20:03:37 +0000 |
commit | 7fa667d94b3bf5c4b7d74835d91d64caac7469dd (patch) | |
tree | 7be3d94a50330f20c1429697ea7b079f4037e267 | |
parent | 41063b188e347b1d600eccbe8220883c51939d02 (diff) | |
download | sonarqube-7fa667d94b3bf5c4b7d74835d91d64caac7469dd.tar.gz sonarqube-7fa667d94b3bf5c4b7d74835d91d64caac7469dd.zip |
SONAR-21467 Fix clickable software quality breakdown links during reindexing
6 files changed, 93 insertions, 37 deletions
diff --git a/server/sonar-web/design-system/src/components/Link.tsx b/server/sonar-web/design-system/src/components/Link.tsx index edd9f192a40..d97489785d7 100644 --- a/server/sonar-web/design-system/src/components/Link.tsx +++ b/server/sonar-web/design-system/src/components/Link.tsx @@ -155,11 +155,14 @@ export const NakedLink = styled(BaseLink)` font-weight: 600; color: ${themeColor('linkNaked')}; - &:hover, - &:focus, - &:active { - color: ${themeColor('linkActive')}; - } + ${({ disabled, theme }) => + disabled + ? tw`sw-cursor-default` + : `&:hover, + &:focus, + &:active { + color: ${themeColor('linkActive')({ theme })}; + }`}; `; export const DrilldownLink = styled(StyledBaseLink)` @@ -219,6 +222,8 @@ export const DiscreetLinkBox = styled(StyledBaseLink)` background-color: none; display: block; } + + ${({ disabled }) => (disabled ? tw`sw-cursor-default` : '')}; `; LinkBox.displayName = 'DiscreetLinkBox'; diff --git a/server/sonar-web/src/main/js/apps/overview/branches/OverallCodeMeasuresPanel.tsx b/server/sonar-web/src/main/js/apps/overview/branches/OverallCodeMeasuresPanel.tsx index 4c228520cc7..1d66c017c3e 100644 --- a/server/sonar-web/src/main/js/apps/overview/branches/OverallCodeMeasuresPanel.tsx +++ b/server/sonar-web/src/main/js/apps/overview/branches/OverallCodeMeasuresPanel.tsx @@ -38,6 +38,7 @@ import { QualityGateStatus } from '../../../types/quality-gates'; import { Component, MeasureEnhanced } from '../../../types/types'; import MeasuresCard from '../components/MeasuresCard'; import MeasuresCardNumber from '../components/MeasuresCardNumber'; +import { OverviewDisabledLinkTooltip } from '../components/OverviewDisabledLinkTooltip'; import { MeasuresTabs } from '../utils'; import MeasuresPanelPercentCards from './MeasuresPanelPercentCards'; import SoftwareImpactMeasureCard from './SoftwareImpactMeasureCard'; @@ -99,6 +100,8 @@ export default function OverallCodeMeasuresPanel(props: Readonly<OverallCodeMeas color={acceptedIssues === '0' ? 'overviewCardDefaultIcon' : 'overviewCardWarningIcon'} /> } + disabled={component.needIssueSync} + tooltip={component.needIssueSync ? <OverviewDisabledLinkTooltip /> : null} > <TextSubdued className="sw-body-xs sw-mt-3"> {intl.formatMessage({ diff --git a/server/sonar-web/src/main/js/apps/overview/branches/SoftwareImpactMeasureBreakdownCard.tsx b/server/sonar-web/src/main/js/apps/overview/branches/SoftwareImpactMeasureBreakdownCard.tsx index d8755ae82ad..fcab9056e65 100644 --- a/server/sonar-web/src/main/js/apps/overview/branches/SoftwareImpactMeasureBreakdownCard.tsx +++ b/server/sonar-web/src/main/js/apps/overview/branches/SoftwareImpactMeasureBreakdownCard.tsx @@ -92,6 +92,7 @@ export function SoftwareImpactMeasureBreakdownCard( }), }, )} + disabled={component.needIssueSync} to={url} > <span>{formatMeasure(value, MetricType.ShortInteger)}</span> diff --git a/server/sonar-web/src/main/js/apps/overview/branches/SoftwareImpactMeasureCard.tsx b/server/sonar-web/src/main/js/apps/overview/branches/SoftwareImpactMeasureCard.tsx index d0ea22018fb..3d7bab789e5 100644 --- a/server/sonar-web/src/main/js/apps/overview/branches/SoftwareImpactMeasureCard.tsx +++ b/server/sonar-web/src/main/js/apps/overview/branches/SoftwareImpactMeasureCard.tsx @@ -29,6 +29,7 @@ import { } from 'design-system'; import * as React from 'react'; import { useIntl } from 'react-intl'; +import Tooltip from '../../../components/controls/Tooltip'; import { DEFAULT_ISSUES_QUERY } from '../../../components/shared/utils'; import { formatMeasure } from '../../../helpers/measures'; import { getComponentIssuesUrl } from '../../../helpers/urls'; @@ -39,6 +40,7 @@ import { } from '../../../types/clean-code-taxonomy'; import { MetricKey, MetricType } from '../../../types/metrics'; import { Component, MeasureEnhanced } from '../../../types/types'; +import { OverviewDisabledLinkTooltip } from '../components/OverviewDisabledLinkTooltip'; import { softwareQualityToMeasure } from '../utils'; import SoftwareImpactMeasureBreakdownCard from './SoftwareImpactMeasureBreakdownCard'; import SoftwareImpactMeasureRating from './SoftwareImpactMeasureRating'; @@ -60,6 +62,8 @@ export function SoftwareImpactMeasureCard(props: Readonly<SoftwareImpactBreakdow const measureRaw = measures.find((m) => m.metric.key === metricKey); const measure = JSON.parse(measureRaw?.value ?? 'null') as SoftwareImpactMeasureData; + const renderDisabled = !measure || component.needIssueSync; + // Find rating measure const ratingMeasure = measures.find((m) => m.metric.key === ratingMetricKey); @@ -85,28 +89,31 @@ export function SoftwareImpactMeasureCard(props: Readonly<SoftwareImpactBreakdow <div className="sw-flex sw-flex-col sw-gap-3"> <div className={classNames('sw-flex sw-gap-1 sw-items-end', { - 'sw-opacity-60': !measure, + 'sw-opacity-60': renderDisabled, })} > {measure ? ( - <NakedLink - data-testid={`overview__software-impact-${softwareQuality}`} - aria-label={intl.formatMessage( - { - id: `overview.measures.software_impact.see_list_of_x_open_issues`, - }, - { - count: measure.total, - softwareQuality: intl.formatMessage({ - id: `software_quality.${softwareQuality}`, - }), - }, - )} - className="sw-text-xl" - to={totalLinkHref} - > - {formatMeasure(measure.total, MetricType.ShortInteger)} - </NakedLink> + <Tooltip overlay={component.needIssueSync ? <OverviewDisabledLinkTooltip /> : null}> + <NakedLink + data-testid={`overview__software-impact-${softwareQuality}`} + aria-label={intl.formatMessage( + { + id: `overview.measures.software_impact.see_list_of_x_open_issues`, + }, + { + count: measure.total, + softwareQuality: intl.formatMessage({ + id: `software_quality.${softwareQuality}`, + }), + }, + )} + className="sw-text-xl" + to={totalLinkHref} + disabled={component.needIssueSync} + > + {formatMeasure(measure.total, MetricType.ShortInteger)} + </NakedLink> + </Tooltip> ) : ( <StyledDash className="sw-self-center sw-font-bold" name="-" /> )} diff --git a/server/sonar-web/src/main/js/apps/overview/branches/__tests__/BranchOverview-it.tsx b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/BranchOverview-it.tsx index a956a8b75e3..e8710eaca52 100644 --- a/server/sonar-web/src/main/js/apps/overview/branches/__tests__/BranchOverview-it.tsx +++ b/server/sonar-web/src/main/js/apps/overview/branches/__tests__/BranchOverview-it.tsx @@ -348,6 +348,41 @@ describe('project overview', () => { false, ]); }); + + it('should disable software impact measure card links during reindexing', async () => { + const { user, ui } = getPageObjects(); + renderBranchOverview({ + component: mockComponent({ + breadcrumbs: [mockComponent({ key: 'foo' })], + key: 'foo', + needIssueSync: true, + }), + }); + + await user.click(await ui.overallCodeButton.find()); + + expect(await ui.softwareImpactMeasureCard(SoftwareQuality.Security).find()).toBeInTheDocument(); + + ui.expectSoftwareImpactMeasureCard( + SoftwareQuality.Security, + 'B', + { + total: 1, + [SoftwareImpactSeverity.High]: 0, + [SoftwareImpactSeverity.Medium]: 1, + [SoftwareImpactSeverity.Low]: 0, + }, + [false, true, false], + ); + + await expect( + byRole('link', { + name: `overview.measures.software_impact.see_list_of_x_open_issues.${1}.software_quality.${ + SoftwareQuality.Security + }`, + }).get(), + ).toHaveATooltipWithContent('indexation.in_progress'); + }); }); describe('application overview', () => { diff --git a/server/sonar-web/src/main/js/apps/overview/components/MeasuresCard.tsx b/server/sonar-web/src/main/js/apps/overview/components/MeasuresCard.tsx index de80ee18c20..68c6174487b 100644 --- a/server/sonar-web/src/main/js/apps/overview/components/MeasuresCard.tsx +++ b/server/sonar-web/src/main/js/apps/overview/components/MeasuresCard.tsx @@ -18,7 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import styled from '@emotion/styled'; -import { Badge, Card, ContentLink, themeBorder, themeColor } from 'design-system'; +import { Badge, Card, ContentLink, Tooltip, themeBorder, themeColor } from 'design-system'; import * as React from 'react'; import { To } from 'react-router-dom'; import { translate, translateWithParameters } from '../../../helpers/l10n'; @@ -32,12 +32,14 @@ export interface MeasuresCardProps { label: string; failed?: boolean; icon?: React.ReactElement; + disabled?: boolean; + tooltip?: React.ReactNode | null; } export default function MeasuresCard( props: React.PropsWithChildren<MeasuresCardProps & React.HTMLAttributes<HTMLDivElement>>, ) { - const { failed, children, metric, icon, value, url, label, ...rest } = props; + const { failed, children, metric, icon, value, url, label, disabled, tooltip, ...rest } = props; return ( <StyledCard className="sw-h-fit sw-p-6 sw-rounded-2 sw-text-base" {...rest}> @@ -49,17 +51,20 @@ export default function MeasuresCard( )} <div className="sw-flex sw-items-center sw-mt-1 sw-justify-between sw-font-semibold"> {value ? ( - <ContentLink - aria-label={translateWithParameters( - 'overview.see_more_details_on_x_of_y', - value, - localizeMetric(metric), - )} - className="it__overview-measures-value sw-text-lg" - to={url} - > - {value} - </ContentLink> + <Tooltip overlay={tooltip}> + <ContentLink + aria-label={translateWithParameters( + 'overview.see_more_details_on_x_of_y', + value, + localizeMetric(metric), + )} + className="it__overview-measures-value sw-text-lg" + to={url} + disabled={disabled} + > + {value} + </ContentLink> + </Tooltip> ) : ( <ColorBold> — </ColorBold> )} |