aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js/apps/overview/pullRequests/PullRequestOverview.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'server/sonar-web/src/main/js/apps/overview/pullRequests/PullRequestOverview.tsx')
-rw-r--r--server/sonar-web/src/main/js/apps/overview/pullRequests/PullRequestOverview.tsx304
1 files changed, 119 insertions, 185 deletions
diff --git a/server/sonar-web/src/main/js/apps/overview/pullRequests/PullRequestOverview.tsx b/server/sonar-web/src/main/js/apps/overview/pullRequests/PullRequestOverview.tsx
index 7bb2f167366..d5be3ce0291 100644
--- a/server/sonar-web/src/main/js/apps/overview/pullRequests/PullRequestOverview.tsx
+++ b/server/sonar-web/src/main/js/apps/overview/pullRequests/PullRequestOverview.tsx
@@ -29,21 +29,20 @@ import {
PageTitle,
TextMuted,
} from 'design-system';
-import { differenceBy, uniq } from 'lodash';
+import { uniq } from 'lodash';
import * as React from 'react';
+import { useEffect, useState } from 'react';
import { FormattedMessage } from 'react-intl';
import { getMeasuresWithMetrics } from '../../../api/measures';
-import { BranchStatusContextInterface } from '../../../app/components/branch-status/BranchStatusContext';
-import withBranchStatus from '../../../app/components/branch-status/withBranchStatus';
-import withBranchStatusActions from '../../../app/components/branch-status/withBranchStatusActions';
import HelpTooltip from '../../../components/controls/HelpTooltip';
import { duplicationRatingConverter } from '../../../components/measure/utils';
import { getBranchLikeQuery } from '../../../helpers/branch-like';
import { translate } from '../../../helpers/l10n';
import { enhanceConditionWithMeasure, enhanceMeasuresWithMetrics } from '../../../helpers/measures';
import { isDefined } from '../../../helpers/types';
-import { getQualityGatesUrl, getQualityGateUrl } from '../../../helpers/urls';
-import { BranchStatusData, PullRequest } from '../../../types/branch-like';
+import { getQualityGateUrl, getQualityGatesUrl } from '../../../helpers/urls';
+import { useBranchStatusQuery } from '../../../queries/branch';
+import { PullRequest } from '../../../types/branch-like';
import { IssueType } from '../../../types/issues';
import { Component, MeasureEnhanced } from '../../../types/types';
import MeasuresPanelIssueMeasure from '../branches/MeasuresPanelIssueMeasure';
@@ -57,73 +56,21 @@ import SonarLintPromotion from '../components/SonarLintPromotion';
import '../styles.css';
import { MeasurementType, PR_METRICS } from '../utils';
-interface Props extends BranchStatusData, Pick<BranchStatusContextInterface, 'fetchBranchStatus'> {
+interface Props {
branchLike: PullRequest;
component: Component;
}
-interface State {
- loading: boolean;
- measures: MeasureEnhanced[];
-}
-
-export class PullRequestOverview extends React.PureComponent<Props, State> {
- mounted = false;
-
- state: State = {
- loading: false,
- measures: [],
- };
+export default function PullRequestOverview(props: Props) {
+ const { component, branchLike } = props;
+ const [loadingMeasure, setLoadingMeasure] = useState(false);
+ const [measures, setMeasures] = useState<MeasureEnhanced[]>([]);
+ const { data: { conditions, ignoredConditions, status } = {}, isLoading } =
+ useBranchStatusQuery(component);
+ const loading = isLoading || loadingMeasure;
- componentDidMount() {
- this.mounted = true;
- if (this.props.conditions === undefined) {
- this.fetchBranchStatusData();
- } else {
- this.fetchBranchData();
- }
- }
-
- componentDidUpdate(prevProps: Props) {
- if (this.conditionsHaveChanged(prevProps)) {
- this.fetchBranchData();
- }
- }
-
- componentWillUnmount() {
- this.mounted = false;
- }
-
- conditionsHaveChanged = (prevProps: Props) => {
- const prevConditions = prevProps.conditions ?? [];
- const newConditions = this.props.conditions ?? [];
- const diff = differenceBy(
- prevConditions.filter((c) => c.level === 'ERROR'),
- newConditions.filter((c) => c.level === 'ERROR'),
- (c) => c.metric
- );
-
- return (
- (prevProps.conditions === undefined && this.props.conditions !== undefined) || diff.length > 0
- );
- };
-
- fetchBranchStatusData = () => {
- const {
- branchLike,
- component: { key },
- } = this.props;
- this.props.fetchBranchStatus(branchLike, key);
- };
-
- fetchBranchData = () => {
- const {
- branchLike,
- component: { key },
- conditions,
- } = this.props;
-
- this.setState({ loading: true });
+ useEffect(() => {
+ setLoadingMeasure(true);
const metricKeys =
conditions !== undefined
@@ -131,153 +78,140 @@ export class PullRequestOverview extends React.PureComponent<Props, State> {
uniq([...PR_METRICS, ...conditions.filter((c) => c.level !== 'OK').map((c) => c.metric)])
: PR_METRICS;
- getMeasuresWithMetrics(key, metricKeys, getBranchLikeQuery(branchLike)).then(
+ getMeasuresWithMetrics(component.key, metricKeys, getBranchLikeQuery(branchLike)).then(
({ component, metrics }) => {
- if (this.mounted && component.measures) {
- this.setState({
- loading: false,
- measures: enhanceMeasuresWithMetrics(component.measures || [], metrics),
- });
+ if (component.measures) {
+ setLoadingMeasure(false);
+ setMeasures(enhanceMeasuresWithMetrics(component.measures || [], metrics));
}
},
() => {
- if (this.mounted) {
- this.setState({ loading: false });
- }
+ setLoadingMeasure(false);
}
);
- };
+ }, [branchLike, component.key, conditions]);
+
+ if (loading) {
+ return (
+ <LargeCenteredLayout>
+ <div className="sw-p-6">
+ <DeferredSpinner loading />
+ </div>
+ </LargeCenteredLayout>
+ );
+ }
- render() {
- const { branchLike, component, conditions, ignoredConditions, status } = this.props;
- const { loading, measures } = this.state;
+ if (conditions === undefined) {
+ return null;
+ }
- if (loading) {
- return (
- <LargeCenteredLayout>
- <div className="sw-p-6">
- <DeferredSpinner loading />
- </div>
- </LargeCenteredLayout>
- );
- }
+ const path =
+ component.qualityGate === undefined
+ ? getQualityGatesUrl()
+ : getQualityGateUrl(component.qualityGate.name);
+
+ const failedConditions = conditions
+ .filter((condition) => condition.level === 'ERROR')
+ .map((c) => enhanceConditionWithMeasure(c, measures))
+ .filter(isDefined);
+
+ return (
+ <LargeCenteredLayout>
+ <div className="it__pr-overview sw-mt-12">
+ <div className="sw-flex">
+ <div className="sw-flex sw-flex-col sw-mr-12 width-30">
+ <QualityGateStatusTitle />
+ <Card>
+ {status && (
+ <QualityGateStatusHeader
+ status={status}
+ failedConditionCount={failedConditions.length}
+ />
+ )}
+
+ <div className="sw-flex sw-items-center sw-mb-4">
+ <TextMuted text={translate('overview.on_new_code_long')} />
+ <HelpTooltip
+ className="sw-ml-2"
+ overlay={
+ <FormattedMessage
+ defaultMessage={translate('overview.quality_gate.conditions_on_new_code')}
+ id="overview.quality_gate.conditions_on_new_code"
+ values={{
+ link: <Link to={path}>{translate('overview.quality_gate')}</Link>,
+ }}
+ />
+ }
+ >
+ <HelperHintIcon aria-label="help-tooltip" />
+ </HelpTooltip>
+ </div>
- if (conditions === undefined) {
- return null;
- }
+ {ignoredConditions && <IgnoredConditionWarning />}
- const path =
- component.qualityGate === undefined
- ? getQualityGatesUrl()
- : getQualityGateUrl(component.qualityGate.name);
+ {status === 'OK' && failedConditions.length === 0 && <QualityGateStatusPassedView />}
- const failedConditions = conditions
- .filter((condition) => condition.level === 'ERROR')
- .map((c) => enhanceConditionWithMeasure(c, measures))
- .filter(isDefined);
+ {status !== 'OK' && <BasicSeparator />}
- return (
- <LargeCenteredLayout>
- <div className="it__pr-overview sw-mt-12">
- <div className="sw-flex">
- <div className="sw-flex sw-flex-col sw-mr-12 width-30">
- <QualityGateStatusTitle />
- <Card>
- {status && (
- <QualityGateStatusHeader
- status={status}
- failedConditionCount={failedConditions.length}
+ {failedConditions.length > 0 && (
+ <div>
+ <QualityGateConditions
+ branchLike={branchLike}
+ collapsible
+ component={component}
+ failedConditions={failedConditions}
/>
- )}
-
- <div className="sw-flex sw-items-center sw-mb-4">
- <TextMuted text={translate('overview.on_new_code_long')} />
- <HelpTooltip
- className="sw-ml-2"
- overlay={
- <FormattedMessage
- defaultMessage={translate('overview.quality_gate.conditions_on_new_code')}
- id="overview.quality_gate.conditions_on_new_code"
- values={{
- link: <Link to={path}>{translate('overview.quality_gate')}</Link>,
- }}
- />
- }
- >
- <HelperHintIcon aria-label="help-tooltip" />
- </HelpTooltip>
</div>
+ )}
+ </Card>
+ <SonarLintPromotion qgConditions={conditions} />
+ </div>
- {ignoredConditions && <IgnoredConditionWarning />}
-
- {status === 'OK' && failedConditions.length === 0 && (
- <QualityGateStatusPassedView />
- )}
-
- {status !== 'OK' && <BasicSeparator />}
-
- {failedConditions.length > 0 && (
- <div>
- <QualityGateConditions
- branchLike={branchLike}
- collapsible
- component={component}
- failedConditions={failedConditions}
- />
- </div>
- )}
- </Card>
- <SonarLintPromotion qgConditions={conditions} />
+ <div className="sw-flex-1">
+ <div className="sw-body-md-highlight">
+ <PageTitle as="h2" text={translate('overview.measures')} />
</div>
- <div className="sw-flex-1">
- <div className="sw-body-md-highlight">
- <PageTitle as="h2" text={translate('overview.measures')} />
- </div>
+ <div className="sw-grid sw-grid-cols-2 sw-gap-4 sw-mt-4">
+ {[
+ IssueType.Bug,
+ IssueType.Vulnerability,
+ IssueType.SecurityHotspot,
+ IssueType.CodeSmell,
+ ].map((type: IssueType) => (
+ <Card key={type} className="sw-p-8">
+ <MeasuresPanelIssueMeasure
+ branchLike={branchLike}
+ component={component}
+ isNewCodeTab
+ measures={measures}
+ type={type}
+ />
+ </Card>
+ ))}
- <div className="sw-grid sw-grid-cols-2 sw-gap-4 sw-mt-4">
- {[
- IssueType.Bug,
- IssueType.Vulnerability,
- IssueType.SecurityHotspot,
- IssueType.CodeSmell,
- ].map((type: IssueType) => (
+ {[MeasurementType.Coverage, MeasurementType.Duplication].map(
+ (type: MeasurementType) => (
<Card key={type} className="sw-p-8">
- <MeasuresPanelIssueMeasure
+ <MeasuresPanelPercentMeasure
branchLike={branchLike}
component={component}
- isNewCodeTab
measures={measures}
+ ratingIcon={renderMeasureIcon(type)}
type={type}
+ useDiffMetric
/>
</Card>
- ))}
-
- {[MeasurementType.Coverage, MeasurementType.Duplication].map(
- (type: MeasurementType) => (
- <Card key={type} className="sw-p-8">
- <MeasuresPanelPercentMeasure
- branchLike={branchLike}
- component={component}
- measures={measures}
- ratingIcon={renderMeasureIcon(type)}
- type={type}
- useDiffMetric
- />
- </Card>
- )
- )}
- </div>
+ )
+ )}
</div>
</div>
</div>
- </LargeCenteredLayout>
- );
- }
+ </div>
+ </LargeCenteredLayout>
+ );
}
-export default withBranchStatus(withBranchStatusActions(PullRequestOverview));
-
function renderMeasureIcon(type: MeasurementType) {
if (type === MeasurementType.Coverage) {
return function CoverageIndicatorRenderer(value?: string) {