aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js/apps
diff options
context:
space:
mode:
authorViktor Vorona <viktor.vorona@sonarsource.com>2024-08-07 10:42:24 +0200
committersonartech <sonartech@sonarsource.com>2024-08-26 20:03:05 +0000
commit764e818569f3328a3d39470ec54837b52117dea5 (patch)
tree532358d9a703869ab8ba24d74f5fd32a7388b600 /server/sonar-web/src/main/js/apps
parent28ff86fcd8312d8e6b61a679106df554bcea072d (diff)
downloadsonarqube-764e818569f3328a3d39470ec54837b52117dea5.tar.gz
sonarqube-764e818569f3328a3d39470ec54837b52117dea5.zip
SONAR-22697 RatingsComponent POC
Diffstat (limited to 'server/sonar-web/src/main/js/apps')
-rw-r--r--server/sonar-web/src/main/js/apps/code/components/ComponentMeasure.tsx22
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/ComponentMeasuresApp.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/components/MeasureHeader.tsx1
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/drilldown/MeasureCell.tsx8
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/sidebar/DomainSubnavigation.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/sidebar/DomainSubnavigationItem.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/sidebar/Sidebar.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/component-measures/sidebar/SubnavigationMeasureValue.tsx4
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/NewCodeMeasuresPanel.tsx10
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/OverallCodeMeasuresPanel.tsx17
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/QualityGateCondition.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/SoftwareImpactMeasureCard.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/overview/branches/SoftwareImpactMeasureRating.tsx57
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/ProjectsList.tsx90
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCard.tsx3
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCardMeasures.tsx21
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/ProjectCardMeasures-test.tsx1
-rw-r--r--server/sonar-web/src/main/js/apps/projects/utils.ts44
-rw-r--r--server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonContainer.tsx6
-rw-r--r--server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSidebarHeader.tsx23
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/Definition.tsx5
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/authentication/AutoProvisionningConsent.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/settings/components/authentication/BitbucketAuthenticationTab.tsx8
24 files changed, 179 insertions, 169 deletions
diff --git a/server/sonar-web/src/main/js/apps/code/components/ComponentMeasure.tsx b/server/sonar-web/src/main/js/apps/code/components/ComponentMeasure.tsx
index d9d9b3251f3..2e76c81919d 100644
--- a/server/sonar-web/src/main/js/apps/code/components/ComponentMeasure.tsx
+++ b/server/sonar-web/src/main/js/apps/code/components/ComponentMeasure.tsx
@@ -17,19 +17,13 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import {
- ContentCell,
- MetricsRatingBadge,
- NumericalCell,
- QualityGateIndicator,
- RatingCell,
- RatingEnum,
-} from 'design-system';
+import { ContentCell, NumericalCell, QualityGateIndicator, RatingCell } from 'design-system';
import * as React from 'react';
import Measure from '~sonar-aligned/components/measure/Measure';
import { formatMeasure } from '~sonar-aligned/helpers/measures';
import { Status } from '~sonar-aligned/types/common';
import { MetricKey, MetricType } from '~sonar-aligned/types/metrics';
+import RatingComponent from '../../../app/components/metrics/RatingComponent';
import { getLeakValue } from '../../../components/measure/utils';
import {
CCT_SOFTWARE_QUALITY_METRICS,
@@ -95,16 +89,18 @@ export default function ComponentMeasure(props: Props) {
case MetricType.Rating:
return (
<RatingCell className="sw-whitespace-nowrap">
- <MetricsRatingBadge
- label={value ?? '—'}
- rating={formatMeasure(value, MetricType.Rating) as RatingEnum}
- />
+ <RatingComponent componentKey={component.key} ratingMetric={metric.key as MetricKey} />
</RatingCell>
);
default:
return (
<NumericalCell className="sw-whitespace-nowrap">
- <Measure metricKey={finalMetricKey} metricType={finalMetricType} value={value} />
+ <Measure
+ componentKey={component.key}
+ metricKey={finalMetricKey}
+ metricType={finalMetricType}
+ value={value}
+ />
</NumericalCell>
);
}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/ComponentMeasuresApp.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/ComponentMeasuresApp.tsx
index 5e77f0dabde..b5c6128f0e7 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/ComponentMeasuresApp.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/ComponentMeasuresApp.tsx
@@ -279,7 +279,7 @@ class ComponentMeasuresApp extends React.PureComponent<Props, State> {
render() {
const { branchLike } = this.props;
const { measures } = this.state;
- const { canBrowseAllChildProjects, qualifier } = this.props.component;
+ const { canBrowseAllChildProjects, qualifier, key } = this.props.component;
const query = parseQuery(this.props.location.query);
const showFullMeasures = hasFullMeasures(branchLike);
const displayOverview = hasBubbleChart(query.metric);
@@ -295,6 +295,7 @@ class ComponentMeasuresApp extends React.PureComponent<Props, State> {
{measures.length > 0 ? (
<div className="sw-grid sw-grid-cols-12 sw-w-full">
<Sidebar
+ componentKey={key}
measures={measures}
selectedMetric={metric ? metric.key : query.metric}
showFullMeasures={showFullMeasures}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureHeader.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureHeader.tsx
index 59bea826a71..ad7ace64e8b 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureHeader.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureHeader.tsx
@@ -63,6 +63,7 @@ export default function MeasureHeader(props: Readonly<Props>) {
<div className="sw-flex sw-items-center sw-ml-2">
<Measure
+ componentKey={component.key}
className={classNames('it__measure-details-value sw-body-md')}
metricKey={metric.key}
metricType={metric.type}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/drilldown/MeasureCell.tsx b/server/sonar-web/src/main/js/apps/component-measures/drilldown/MeasureCell.tsx
index 2768f777df9..61fbe5405a5 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/drilldown/MeasureCell.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/drilldown/MeasureCell.tsx
@@ -38,7 +38,13 @@ export default function MeasureCell({ component, measure, metric }: Readonly<Pro
return (
<NumericalCell className="sw-py-3">
- <Measure metricKey={metric.key} metricType={metric.type} value={value} small />
+ <Measure
+ componentKey={component.key}
+ metricKey={metric.key}
+ metricType={metric.type}
+ value={value}
+ small
+ />
</NumericalCell>
);
}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/sidebar/DomainSubnavigation.tsx b/server/sonar-web/src/main/js/apps/component-measures/sidebar/DomainSubnavigation.tsx
index ae2fd362713..a91187072a0 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/sidebar/DomainSubnavigation.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/sidebar/DomainSubnavigation.tsx
@@ -42,6 +42,7 @@ import {
import DomainSubnavigationItem from './DomainSubnavigationItem';
interface Props {
+ componentKey: string;
domain: { measures: MeasureEnhanced[]; name: string };
onChange: (metric: string) => void;
open: boolean;
@@ -50,7 +51,7 @@ interface Props {
}
export default function DomainSubnavigation(props: Readonly<Props>) {
- const { domain, onChange, open, selected, showFullMeasures } = props;
+ const { componentKey, domain, onChange, open, selected, showFullMeasures } = props;
const helperMessageKey = `component_measures.domain_subnavigation.${domain.name}.help`;
const helper = hasMessage(helperMessageKey) ? translate(helperMessageKey) : undefined;
const items = addMeasureCategories(domain.name, domain.measures);
@@ -106,6 +107,7 @@ export default function DomainSubnavigation(props: Readonly<Props>) {
) : (
<DomainSubnavigationItem
key={item.metric.key}
+ componentKey={componentKey}
measure={item}
name={getMetricSubnavigationName(item.metric, translateMetric)}
onChange={onChange}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/sidebar/DomainSubnavigationItem.tsx b/server/sonar-web/src/main/js/apps/component-measures/sidebar/DomainSubnavigationItem.tsx
index 791b6025f32..cfb2184ff25 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/sidebar/DomainSubnavigationItem.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/sidebar/DomainSubnavigationItem.tsx
@@ -23,6 +23,7 @@ import { MeasureEnhanced } from '../../../types/types';
import SubnavigationMeasureValue from './SubnavigationMeasureValue';
interface Props {
+ componentKey: string;
measure: MeasureEnhanced;
name: string;
onChange: (metric: string) => void;
@@ -30,6 +31,7 @@ interface Props {
}
export default function DomainSubnavigationItem({
+ componentKey,
measure,
name,
onChange,
@@ -47,7 +49,7 @@ export default function DomainSubnavigationItem({
id={`measure-${key}-name`}
>
{name}
- <SubnavigationMeasureValue measure={measure} />
+ <SubnavigationMeasureValue measure={measure} componentKey={componentKey} />
</SubnavigationItem>
);
}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/sidebar/Sidebar.tsx b/server/sonar-web/src/main/js/apps/component-measures/sidebar/Sidebar.tsx
index 2370040305b..9a3cf5fb6b8 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/sidebar/Sidebar.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/sidebar/Sidebar.tsx
@@ -38,6 +38,7 @@ import { PROJECT_OVERVEW, Query, isProjectOverview, populateDomainsFromMeasures
import DomainSubnavigation from './DomainSubnavigation';
interface Props {
+ componentKey: string;
measures: MeasureEnhanced[];
selectedMetric: string;
showFullMeasures: boolean;
@@ -45,7 +46,7 @@ interface Props {
}
export default function Sidebar(props: Readonly<Props>) {
- const { showFullMeasures, updateQuery, selectedMetric, measures } = props;
+ const { showFullMeasures, updateQuery, componentKey, selectedMetric, measures } = props;
const { top: topScroll, scrolledOnce } = useFollowScroll();
const domains = populateDomainsFromMeasures(measures);
@@ -99,6 +100,7 @@ export default function Sidebar(props: Readonly<Props>) {
{domains.map((domain: Domain) => (
<DomainSubnavigation
+ componentKey={componentKey}
domain={domain}
key={domain.name}
onChange={handleChangeMetric}
diff --git a/server/sonar-web/src/main/js/apps/component-measures/sidebar/SubnavigationMeasureValue.tsx b/server/sonar-web/src/main/js/apps/component-measures/sidebar/SubnavigationMeasureValue.tsx
index 9079b998aca..3ac980e5bea 100644
--- a/server/sonar-web/src/main/js/apps/component-measures/sidebar/SubnavigationMeasureValue.tsx
+++ b/server/sonar-web/src/main/js/apps/component-measures/sidebar/SubnavigationMeasureValue.tsx
@@ -24,10 +24,11 @@ import { isDiffMetric } from '../../../helpers/measures';
import { MeasureEnhanced } from '../../../types/types';
interface Props {
+ componentKey: string;
measure: MeasureEnhanced;
}
-export default function SubnavigationMeasureValue({ measure }: Readonly<Props>) {
+export default function SubnavigationMeasureValue({ measure, componentKey }: Readonly<Props>) {
const isDiff = isDiffMetric(measure.metric.key);
const value = isDiff ? measure.leak : measure.value;
@@ -37,6 +38,7 @@ export default function SubnavigationMeasureValue({ measure }: Readonly<Props>)
id={`measure-${measure.metric.key}-${isDiff ? 'leak' : 'value'}`}
>
<Measure
+ componentKey={componentKey}
badgeSize="xs"
metricKey={measure.metric.key}
metricType={measure.metric.type}
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/NewCodeMeasuresPanel.tsx b/server/sonar-web/src/main/js/apps/overview/branches/NewCodeMeasuresPanel.tsx
index a6b421c8e38..cd576b12c52 100644
--- a/server/sonar-web/src/main/js/apps/overview/branches/NewCodeMeasuresPanel.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/branches/NewCodeMeasuresPanel.tsx
@@ -21,7 +21,6 @@ import styled from '@emotion/styled';
import classNames from 'classnames';
import {
LightLabel,
- MetricsRatingBadge,
NoDataIcon,
SnoozeCircleIcon,
TextError,
@@ -39,10 +38,11 @@ import {
getComponentSecurityHotspotsUrl,
} from '~sonar-aligned/helpers/urls';
import { MetricKey, MetricType } from '~sonar-aligned/types/metrics';
+import RatingComponent from '../../../app/components/metrics/RatingComponent';
import { getLeakValue } from '../../../components/measure/utils';
import { DEFAULT_ISSUES_QUERY } from '../../../components/shared/utils';
import { translate } from '../../../helpers/l10n';
-import { findMeasure, formatRating, isDiffMetric } from '../../../helpers/measures';
+import { findMeasure, isDiffMetric } from '../../../helpers/measures';
import { CodeScope, getComponentDrilldownUrl } from '../../../helpers/urls';
import { ApplicationPeriod } from '../../../types/application';
import { Branch } from '../../../types/branch-like';
@@ -291,9 +291,9 @@ export default function NewCodeMeasuresPanel(props: Readonly<Props>) {
showRequired={!isApp}
icon={
newSecurityReviewRating ? (
- <MetricsRatingBadge
- label={newSecurityReviewRating}
- rating={formatRating(newSecurityReviewRating)}
+ <RatingComponent
+ componentKey={component.key}
+ ratingMetric={MetricKey.new_security_review_rating}
size="md"
/>
) : (
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 fa5c11cd111..a669f5ce1be 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
@@ -18,13 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import classNames from 'classnames';
-import {
- MetricsRatingBadge,
- NoDataIcon,
- SnoozeCircleIcon,
- TextSubdued,
- getTabPanelId,
-} from 'design-system';
+import { NoDataIcon, SnoozeCircleIcon, TextSubdued, getTabPanelId } from 'design-system';
import * as React from 'react';
import { useIntl } from 'react-intl';
import { getBranchLikeQuery } from '~sonar-aligned/helpers/branch-like';
@@ -34,7 +28,8 @@ import {
getComponentSecurityHotspotsUrl,
} from '~sonar-aligned/helpers/urls';
import { MetricKey, MetricType } from '~sonar-aligned/types/metrics';
-import { findMeasure, formatRating, isDiffMetric } from '../../../helpers/measures';
+import RatingComponent from '../../../app/components/metrics/RatingComponent';
+import { findMeasure, isDiffMetric } from '../../../helpers/measures';
import { CodeScope, getComponentDrilldownUrl } from '../../../helpers/urls';
import { Branch } from '../../../types/branch-like';
import { SoftwareQuality } from '../../../types/clean-code-taxonomy';
@@ -199,9 +194,9 @@ export default function OverallCodeMeasuresPanel(props: Readonly<OverallCodeMeas
showRequired={!isApp}
icon={
securityRating ? (
- <MetricsRatingBadge
- label={securityRating}
- rating={formatRating(securityRating)}
+ <RatingComponent
+ componentKey={component.key}
+ ratingMetric={MetricKey.security_review_rating}
size="md"
/>
) : (
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/QualityGateCondition.tsx b/server/sonar-web/src/main/js/apps/overview/branches/QualityGateCondition.tsx
index 9cad5268f98..3563e8d3865 100644
--- a/server/sonar-web/src/main/js/apps/overview/branches/QualityGateCondition.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/branches/QualityGateCondition.tsx
@@ -145,7 +145,7 @@ export class QualityGateCondition extends React.PureComponent<Props> {
};
render() {
- const { condition } = this.props;
+ const { condition, component } = this.props;
const { measure } = condition;
const { metric } = measure;
@@ -159,6 +159,7 @@ export class QualityGateCondition extends React.PureComponent<Props> {
<MeasureIndicator
className="sw-flex sw-justify-center sw-w-6 sw-mx-4"
decimals={2}
+ componentKey={component.key}
metricKey={measure.metric.key}
metricType={measure.metric.type}
value={actual}
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 420f7e65e9e..17800780d9b 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
@@ -65,9 +65,6 @@ export function SoftwareImpactMeasureCard(props: Readonly<SoftwareImpactBreakdow
(m) => m.metric.key === SOFTWARE_QUALITIES_METRIC_KEYS_MAP[softwareQuality].deprecatedMetric,
);
- // Find rating measure
- const ratingMeasure = measures.find((m) => m.metric.key === ratingMetricKey);
-
const count = formatMeasure(measure?.total ?? alternativeMeasure?.value, MetricType.ShortInteger);
const totalLinkHref = getComponentIssuesUrl(component.key, {
@@ -140,7 +137,8 @@ export function SoftwareImpactMeasureCard(props: Readonly<SoftwareImpactBreakdow
<div className="sw-flex-grow sw-flex sw-justify-end">
<SoftwareImpactMeasureRating
softwareQuality={softwareQuality}
- value={ratingMeasure?.value}
+ componentKey={component.key}
+ ratingMetricKey={ratingMetricKey}
/>
</div>
</div>
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/SoftwareImpactMeasureRating.tsx b/server/sonar-web/src/main/js/apps/overview/branches/SoftwareImpactMeasureRating.tsx
index 7fe77dd89a3..cc1790b9f95 100644
--- a/server/sonar-web/src/main/js/apps/overview/branches/SoftwareImpactMeasureRating.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/branches/SoftwareImpactMeasureRating.tsx
@@ -17,50 +17,47 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { Tooltip } from '@sonarsource/echoes-react';
-import { MetricsRatingBadge } from 'design-system';
import * as React from 'react';
-import { useIntl } from 'react-intl';
-import { formatRating } from '../../../helpers/measures';
+import RatingComponent from '../../../app/components/metrics/RatingComponent';
+import { MetricKey } from '../../../sonar-aligned/types/metrics';
import { SoftwareQuality } from '../../../types/clean-code-taxonomy';
-import SoftwareImpactRatingTooltipContent from './SoftwareImpactRatingTooltip';
export interface SoftwareImpactMeasureRatingProps {
+ componentKey: string;
+ ratingMetricKey: MetricKey;
softwareQuality: SoftwareQuality;
- value?: string;
}
export function SoftwareImpactMeasureRating(props: Readonly<SoftwareImpactMeasureRatingProps>) {
- const { softwareQuality, value } = props;
+ const { ratingMetricKey, componentKey } = props;
- const intl = useIntl();
+ // const intl = useIntl();
- const rating = formatRating(value);
-
- const additionalInfo = (
- <SoftwareImpactRatingTooltipContent rating={rating} softwareQuality={softwareQuality} />
- );
+ // const additionalInfo = (
+ // <SoftwareImpactRatingTooltipContent rating={rating} softwareQuality={softwareQuality} />
+ // );
return (
<>
- <Tooltip content={additionalInfo}>
- <MetricsRatingBadge
- size="md"
- className="sw-text-sm"
- rating={rating}
- label={intl.formatMessage(
- {
- id: 'overview.project.software_impact.has_rating',
- },
- {
- softwareQuality: intl.formatMessage({ id: `software_quality.${softwareQuality}` }),
- rating,
- },
- )}
- />
- </Tooltip>
+ {/* <Tooltip content={additionalInfo}> */}
+ <RatingComponent
+ size="md"
+ className="sw-text-sm"
+ ratingMetric={ratingMetricKey}
+ componentKey={componentKey}
+ // label={intl.formatMessage(
+ // {
+ // id: 'overview.project.software_impact.has_rating',
+ // },
+ // {
+ // softwareQuality: intl.formatMessage({ id: `software_quality.${softwareQuality}` }),
+ // rating,
+ // },
+ // )}
+ />
+ {/* </Tooltip> */}
{/* The badge is not interactive, so show the tooltip content for screen-readers only */}
- <span className="sw-sr-only">{additionalInfo}</span>
+ {/* <span className="sw-sr-only">{additionalInfo}</span> */}
</>
);
}
diff --git a/server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx b/server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx
index e43f16359de..f7fe5e311bd 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx
+++ b/server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx
@@ -66,7 +66,7 @@ interface State {
facets?: Facets;
loading: boolean;
pageIndex?: number;
- projects?: Project[];
+ projects?: Omit<Project, 'measures'>[];
query: Query;
total?: number;
}
diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectsList.tsx b/server/sonar-web/src/main/js/apps/projects/components/ProjectsList.tsx
index 3b680c306f1..4b255c17b3c 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/ProjectsList.tsx
+++ b/server/sonar-web/src/main/js/apps/projects/components/ProjectsList.tsx
@@ -18,17 +18,20 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { Spinner } from '@sonarsource/echoes-react';
import classNames from 'classnames';
-import { Spinner } from 'design-system';
import * as React from 'react';
import { AutoSizer } from 'react-virtualized/dist/commonjs/AutoSizer';
import { List, ListRowProps } from 'react-virtualized/dist/commonjs/List';
import EmptySearch from '../../../components/common/EmptySearch';
import ListFooter from '../../../components/controls/ListFooter';
import { translate } from '../../../helpers/l10n';
+import { isDiffMetric } from '../../../helpers/measures';
+import { useMeasuresForProjectsQuery } from '../../../queries/measures';
import { CurrentUser } from '../../../types/users';
import { Query } from '../query';
import { Project } from '../types';
+import { defineMetrics } from '../utils';
import EmptyFavoriteSearch from './EmptyFavoriteSearch';
import EmptyInstance from './EmptyInstance';
import NoFavoriteProjects from './NoFavoriteProjects';
@@ -46,29 +49,40 @@ interface Props {
isFiltered: boolean;
loadMore: () => void;
loading: boolean;
- projects: Project[];
+ projects: Omit<Project, 'measures'>[];
query: Query;
total?: number;
}
-export default class ProjectsList extends React.PureComponent<Props> {
- renderNoProjects() {
- const { currentUser, isFavorite, isFiltered, query } = this.props;
- if (isFiltered) {
- return isFavorite ? <EmptyFavoriteSearch query={query} /> : <EmptySearch />;
- }
- return isFavorite ? <NoFavoriteProjects /> : <EmptyInstance currentUser={currentUser} />;
- }
+export default function ProjectsList(props: Readonly<Props>) {
+ const {
+ currentUser,
+ isFavorite,
+ handleFavorite,
+ cardType,
+ isFiltered,
+ query,
+ loading,
+ projects,
+ total,
+ loadMore,
+ } = props;
+ const { data: measures, isLoading: measuresLoading } = useMeasuresForProjectsQuery(
+ {
+ projectKeys: projects.map((p) => p.key),
+ metricKeys: defineMetrics(query),
+ },
+ { enabled: projects.length > 0 },
+ );
- renderRow = ({ index, key, style }: ListRowProps) => {
- const { loading, projects, total } = this.props;
+ const renderRow = ({ index, key, style }: ListRowProps) => {
if (index === projects.length) {
return (
<div key="footer" style={{ ...style }}>
<ListFooter
loadMoreAriaLabel={translate('projects.show_more')}
count={projects !== undefined ? projects.length : 0}
- loadMore={this.props.loadMore}
+ loadMore={loadMore}
loading={loading}
ready={!loading}
total={total ?? 0}
@@ -78,6 +92,19 @@ export default class ProjectsList extends React.PureComponent<Props> {
}
const project = projects[index];
+ const componentMeasures =
+ measures
+ ?.filter((measure) => measure.component === project.key)
+ .reduce(
+ (acc, measure) => {
+ const value = isDiffMetric(measure.metric) ? measure.period?.value : measure.value;
+ if (value !== undefined) {
+ acc[measure.metric] = value;
+ }
+ return acc;
+ },
+ {} as Record<string, string>,
+ ) ?? {};
return (
<div
@@ -88,53 +115,52 @@ export default class ProjectsList extends React.PureComponent<Props> {
>
<div className="sw-h-full" role="gridcell">
<ProjectCard
- currentUser={this.props.currentUser}
- handleFavorite={this.props.handleFavorite}
+ currentUser={currentUser}
+ handleFavorite={handleFavorite}
key={project.key}
- project={project}
- type={this.props.cardType}
+ project={{ ...project, measures: componentMeasures }}
+ type={cardType}
/>
</div>
</div>
);
};
- renderList() {
- return this.props.loading ? (
- <Spinner />
- ) : (
+ if (projects.length === 0) {
+ if (isFiltered) {
+ return isFavorite ? <EmptyFavoriteSearch query={query} /> : <EmptySearch />;
+ }
+ return isFavorite ? <NoFavoriteProjects /> : <EmptyInstance currentUser={currentUser} />;
+ }
+
+ return (
+ <Spinner isLoading={loading || measuresLoading}>
<AutoSizer>
{({ height, width }) => (
<List
aria-label={translate('project_plural')}
height={height}
overscanRowCount={2}
- rowCount={this.props.projects.length + 1}
+ rowCount={projects.length + 1}
rowHeight={({ index }) => {
if (index === 0) {
// first card, double top and bottom margin
return PROJECT_CARD_HEIGHT + PROJECT_CARD_MARGIN * 2;
}
- if (index === this.props.projects.length) {
+ if (index === projects.length) {
// Footer card, no margin
return PROJECT_LIST_FOOTER_HEIGHT;
}
// all other cards, only bottom margin
return PROJECT_CARD_HEIGHT + PROJECT_CARD_MARGIN;
}}
- rowRenderer={this.renderRow}
+ rowRenderer={renderRow}
style={{ outline: 'none' }}
tabIndex={-1}
width={width}
/>
)}
</AutoSizer>
- );
- }
-
- render() {
- const { projects } = this.props;
-
- return projects.length > 0 ? this.renderList() : this.renderNoProjects();
- }
+ </Spinner>
+ );
}
diff --git a/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCard.tsx b/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCard.tsx
index d2e64fd1476..d0ff2f6e32f 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCard.tsx
+++ b/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCard.tsx
@@ -172,6 +172,7 @@ function renderFirstLine(
<div>
<span className="sw-body-sm-highlight sw-mr-1" data-key={MetricKey.new_lines}>
<Measure
+ componentKey={key}
metricKey={MetricKey.new_lines}
metricType={MetricType.ShortInteger}
value={measures.new_lines}
@@ -189,6 +190,7 @@ function renderFirstLine(
<div>
<span className="sw-body-sm-highlight sw-mr-1" data-key={MetricKey.ncloc}>
<Measure
+ componentKey={key}
metricKey={MetricKey.ncloc}
metricType={MetricType.ShortInteger}
value={measures.ncloc}
@@ -237,6 +239,7 @@ function renderSecondLine(
<ProjectCardMeasures
measures={measures}
componentQualifier={qualifier}
+ componentKey={key}
isNewCode={isNewCode}
/>
);
diff --git a/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCardMeasures.tsx b/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCardMeasures.tsx
index e7cfe3abf6b..f444465e33b 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCardMeasures.tsx
+++ b/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCardMeasures.tsx
@@ -20,30 +20,30 @@
import {
CoverageIndicator,
DuplicationsIndicator,
- MetricsRatingBadge,
Note,
PageContentFontWrapper,
- RatingLabel,
} from 'design-system';
import * as React from 'react';
import Measure from '~sonar-aligned/components/measure/Measure';
import { ComponentQualifier } from '~sonar-aligned/types/component';
import { MetricKey, MetricType } from '~sonar-aligned/types/metrics';
+import RatingComponent from '../../../../app/components/metrics/RatingComponent';
import { duplicationRatingConverter } from '../../../../components/measure/utils';
import { translate } from '../../../../helpers/l10n';
-import { formatRating } from '../../../../helpers/measures';
import { isDefined } from '../../../../helpers/types';
import { Dict } from '../../../../types/types';
import ProjectCardMeasure from './ProjectCardMeasure';
export interface ProjectCardMeasuresProps {
+ // eslint-disable-next-line react/no-unused-prop-types
+ componentKey: string;
componentQualifier: ComponentQualifier;
isNewCode: boolean;
measures: Dict<string | undefined>;
}
function renderNewIssues(props: ProjectCardMeasuresProps) {
- const { measures, isNewCode } = props;
+ const { measures, isNewCode, componentKey } = props;
if (!isNewCode) {
return null;
@@ -55,6 +55,7 @@ function renderNewIssues(props: ProjectCardMeasuresProps) {
label={translate(`metric.${MetricKey.new_violations}.description`)}
>
<Measure
+ componentKey={componentKey}
metricKey={MetricKey.new_violations}
metricType={MetricType.ShortInteger}
value={measures[MetricKey.new_violations]}
@@ -65,7 +66,7 @@ function renderNewIssues(props: ProjectCardMeasuresProps) {
}
function renderCoverage(props: ProjectCardMeasuresProps) {
- const { measures, isNewCode } = props;
+ const { measures, isNewCode, componentKey } = props;
const coverageMetric = isNewCode ? MetricKey.new_coverage : MetricKey.coverage;
return (
@@ -73,6 +74,7 @@ function renderCoverage(props: ProjectCardMeasuresProps) {
<div>
{measures[coverageMetric] && <CoverageIndicator value={measures[coverageMetric]} />}
<Measure
+ componentKey={componentKey}
metricKey={coverageMetric}
metricType={MetricType.Percent}
value={measures[coverageMetric]}
@@ -84,7 +86,7 @@ function renderCoverage(props: ProjectCardMeasuresProps) {
}
function renderDuplication(props: ProjectCardMeasuresProps) {
- const { measures, isNewCode } = props;
+ const { measures, isNewCode, componentKey } = props;
const duplicationMetric = isNewCode
? MetricKey.new_duplicated_lines_density
: MetricKey.duplicated_lines_density;
@@ -102,6 +104,7 @@ function renderDuplication(props: ProjectCardMeasuresProps) {
<div>
{measures[duplicationMetric] != null && <DuplicationsIndicator rating={rating} />}
<Measure
+ componentKey={componentKey}
metricKey={duplicationMetric}
metricType={MetricType.Percent}
value={measures[duplicationMetric]}
@@ -113,7 +116,7 @@ function renderDuplication(props: ProjectCardMeasuresProps) {
}
function renderRatings(props: ProjectCardMeasuresProps) {
- const { isNewCode, measures } = props;
+ const { isNewCode, measures, componentKey } = props;
const measuresByCodeLeak = isNewCode
? []
@@ -165,7 +168,6 @@ function renderRatings(props: ProjectCardMeasuresProps) {
return measureList.map((measure) => {
const { iconLabel, metricKey, metricRatingKey, metricType } = measure;
- const value = formatRating(measures[metricRatingKey]);
const measureValue =
[
@@ -178,8 +180,9 @@ function renderRatings(props: ProjectCardMeasuresProps) {
return (
<ProjectCardMeasure key={metricKey} metricKey={metricKey} label={iconLabel}>
- <MetricsRatingBadge label={metricKey} rating={value as RatingLabel} />
+ <RatingComponent ratingMetric={metricRatingKey} componentKey={componentKey} />
<Measure
+ componentKey={componentKey}
metricKey={metricKey}
metricType={metricType}
value={measureValue}
diff --git a/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/ProjectCardMeasures-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/ProjectCardMeasures-test.tsx
index c2bc48d9936..afbe8925074 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/ProjectCardMeasures-test.tsx
+++ b/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/ProjectCardMeasures-test.tsx
@@ -92,6 +92,7 @@ function renderProjectCardMeasures(
renderComponent(
<ProjectCardMeasures
+ componentKey="test"
componentQualifier={ComponentQualifier.Project}
isNewCode={false}
measures={measures}
diff --git a/server/sonar-web/src/main/js/apps/projects/utils.ts b/server/sonar-web/src/main/js/apps/projects/utils.ts
index 854d057f476..fa442cc28c4 100644
--- a/server/sonar-web/src/main/js/apps/projects/utils.ts
+++ b/server/sonar-web/src/main/js/apps/projects/utils.ts
@@ -20,9 +20,7 @@
import { invert } from 'lodash';
import { MetricKey } from '~sonar-aligned/types/metrics';
import { Facet, getScannableProjects, searchProjects } from '../../api/components';
-import { getMeasuresForProjects } from '../../api/measures';
import { translate, translateWithParameters } from '../../helpers/l10n';
-import { isDiffMetric } from '../../helpers/measures';
import { RequestData } from '../../helpers/request';
import { Dict } from '../../types/types';
import { Query, convertToFilter } from './query';
@@ -192,33 +190,14 @@ export function fetchProjects({
});
return searchProjects(data)
- .then((response) =>
- Promise.all([
- fetchProjectMeasures(response.components, query),
- Promise.resolve(response),
- fetchScannableProjects(),
- ]),
- )
- .then(([measures, { components, facets, paging }, { scannableProjects }]) => {
+ .then((response) => Promise.all([Promise.resolve(response), fetchScannableProjects()]))
+ .then(([{ components, facets, paging }, { scannableProjects }]) => {
return {
facets: getFacetsMap(facets),
- projects: components.map((component) => {
- const componentMeasures: Dict<string> = {};
- measures
- .filter((measure) => measure.component === component.key)
- .forEach((measure) => {
- const value = isDiffMetric(measure.metric) ? measure.period?.value : measure.value;
- if (value !== undefined) {
- componentMeasures[measure.metric] = value;
- }
- });
-
- return {
- ...component,
- measures: componentMeasures,
- isScannable: scannableProjects.find((p) => p.key === component.key) !== undefined,
- };
- }),
+ projects: components.map((component) => ({
+ ...component,
+ isScannable: scannableProjects.find((p) => p.key === component.key) !== undefined,
+ })),
total: paging.total,
};
});
@@ -260,17 +239,6 @@ export function convertToQueryData(query: Query, isFavorite: boolean, defaultDat
return data;
}
-export function fetchProjectMeasures(projects: Array<{ key: string }>, query: Query) {
- if (!projects.length) {
- return Promise.resolve([]);
- }
-
- const projectKeys = projects.map((project) => project.key);
- const metrics = defineMetrics(query);
-
- return getMeasuresForProjects(projectKeys, metrics);
-}
-
function mapFacetValues(values: Array<{ count: number; val: string }>) {
const map: Dict<number> = {};
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonContainer.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonContainer.tsx
index aed914a746c..6825ad39b45 100644
--- a/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonContainer.tsx
+++ b/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonContainer.tsx
@@ -38,9 +38,9 @@ export function ComparisonContainer(props: Readonly<Props>) {
const { profile, profiles } = props;
const location = useLocation();
const router = useRouter();
- const { data: inheritRulesSetting } = useGetValueQuery(
- SettingsKey.QPAdminCanDisableInheritedRules,
- );
+ const { data: inheritRulesSetting } = useGetValueQuery({
+ key: SettingsKey.QPAdminCanDisableInheritedRules,
+ });
const canDeactivateInheritedRules = inheritRulesSetting?.value === 'true';
const { withKey } = location.query;
diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSidebarHeader.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSidebarHeader.tsx
index b640a000212..ef2941c81d3 100644
--- a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSidebarHeader.tsx
+++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSidebarHeader.tsx
@@ -81,16 +81,19 @@ function HotspotSidebarHeader(props: SecurityHotspotsAppRendererProps) {
<CoverageIndicator value={hotspotsReviewedMeasure} />
)}
- <Measure
- className="it__hs-review-percentage sw-body-sm-highlight sw-ml-2"
- metricKey={
- isBranch(branchLike) && !filters.inNewCodePeriod
- ? MetricKey.security_hotspots_reviewed
- : MetricKey.new_security_hotspots_reviewed
- }
- metricType={MetricType.Percent}
- value={hotspotsReviewedMeasure}
- />
+ {component && (
+ <Measure
+ className="it__hs-review-percentage sw-body-sm-highlight sw-ml-2"
+ componentKey={component.key}
+ metricKey={
+ isBranch(branchLike) && !filters.inNewCodePeriod
+ ? MetricKey.security_hotspots_reviewed
+ : MetricKey.new_security_hotspots_reviewed
+ }
+ metricType={MetricType.Percent}
+ value={hotspotsReviewedMeasure}
+ />
+ )}
</Spinner>
<span className="sw-body-sm sw-ml-1">
diff --git a/server/sonar-web/src/main/js/apps/settings/components/Definition.tsx b/server/sonar-web/src/main/js/apps/settings/components/Definition.tsx
index d8379e27c81..c8180e203fc 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/Definition.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/Definition.tsx
@@ -57,7 +57,10 @@ export default function Definition(props: Readonly<Props>) {
const [success, setSuccess] = React.useState(false);
const [changedValue, setChangedValue] = React.useState<FieldValue>();
const [validationMessage, setValidationMessage] = React.useState<string>();
- const { data: loadedSettingValue, isLoading } = useGetValueQuery(definition.key, component?.key);
+ const { data: loadedSettingValue, isLoading } = useGetValueQuery({
+ key: definition.key,
+ component: component?.key,
+ });
const settingValue = isLoading ? initialSettingValue : loadedSettingValue ?? undefined;
const { mutateAsync: resetSettingValue } = useResetSettingsMutation();
diff --git a/server/sonar-web/src/main/js/apps/settings/components/authentication/AutoProvisionningConsent.tsx b/server/sonar-web/src/main/js/apps/settings/components/authentication/AutoProvisionningConsent.tsx
index f6c20eaa44b..85281797e08 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/authentication/AutoProvisionningConsent.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/authentication/AutoProvisionningConsent.tsx
@@ -44,7 +44,7 @@ export default function AutoProvisioningConsent(props: Readonly<Props>) {
const { mutate: updateGithubConfig } = useUpdateGitHubConfigurationMutation();
const { mutate: updateGitlabConfig } = useUpdateGitLabConfigurationMutation();
- const { data: userConsent } = useGetValueQuery(CONSENT_SETTING_KEY);
+ const { data: userConsent } = useGetValueQuery({ key: CONSENT_SETTING_KEY });
const { mutateAsync: resetSettingValue } = useResetSettingsMutation();
if (
diff --git a/server/sonar-web/src/main/js/apps/settings/components/authentication/BitbucketAuthenticationTab.tsx b/server/sonar-web/src/main/js/apps/settings/components/authentication/BitbucketAuthenticationTab.tsx
index 138817182b1..06a2ed8703d 100644
--- a/server/sonar-web/src/main/js/apps/settings/components/authentication/BitbucketAuthenticationTab.tsx
+++ b/server/sonar-web/src/main/js/apps/settings/components/authentication/BitbucketAuthenticationTab.tsx
@@ -37,10 +37,10 @@ interface Props {
export default function BitbucketAuthenticationTab(props: Readonly<Props>) {
const { definitions } = props;
- const { data: allowToSignUpEnabled } = useGetValueQuery(
- 'sonar.auth.bitbucket.allowUsersToSignUp',
- );
- const { data: workspaces } = useGetValueQuery('sonar.auth.bitbucket.workspaces');
+ const { data: allowToSignUpEnabled } = useGetValueQuery({
+ key: 'sonar.auth.bitbucket.allowUsersToSignUp',
+ });
+ const { data: workspaces } = useGetValueQuery({ key: 'sonar.auth.bitbucket.workspaces' });
const isConfigurationUnsafe =
allowToSignUpEnabled?.value === 'true' &&