diff options
author | stanislavh <stanislav.honcharov@sonarsource.com> | 2024-01-23 11:41:55 +0100 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2024-01-26 20:02:46 +0000 |
commit | 21dc2dcb8b252123dd31eba496d5dde4f28351af (patch) | |
tree | 977f01a2251b477ef8f6eefe4de05627acb5b738 | |
parent | a9da6c250efca1eb81ae0d172a637785f7900a41 (diff) | |
download | sonarqube-21dc2dcb8b252123dd31eba496d5dde4f28351af.tar.gz sonarqube-21dc2dcb8b252123dd31eba496d5dde4f28351af.zip |
SONAR-21467 Adopt new 2 column layout for branch overview
9 files changed, 132 insertions, 99 deletions
diff --git a/server/sonar-web/src/main/js/apps/overview/branches/BranchOverviewRenderer.tsx b/server/sonar-web/src/main/js/apps/overview/branches/BranchOverviewRenderer.tsx index 0dca8d3241e..9c6e4415766 100644 --- a/server/sonar-web/src/main/js/apps/overview/branches/BranchOverviewRenderer.tsx +++ b/server/sonar-web/src/main/js/apps/overview/branches/BranchOverviewRenderer.tsx @@ -17,7 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { BasicSeparator, LargeCenteredLayout, PageContentFontWrapper } from 'design-system'; +import { BasicSeparator, Card, LargeCenteredLayout, PageContentFontWrapper } from 'design-system'; import * as React from 'react'; import A11ySkipTarget from '../../../components/a11y/A11ySkipTarget'; import { useLocation } from '../../../components/hoc/withRouter'; @@ -31,6 +31,7 @@ import { Analysis, GraphType, MeasureHistory } from '../../../types/project-acti import { QualityGateStatus } from '../../../types/quality-gates'; import { Component, MeasureEnhanced, Metric, Period, QualityGate } from '../../../types/types'; import { AnalysisStatus } from '../components/AnalysisStatus'; +import SonarLintPromotion from '../components/SonarLintPromotion'; import { MeasuresTabs } from '../utils'; import AcceptedIssuesPanel from './AcceptedIssuesPanel'; import ActivityPanel from './ActivityPanel'; @@ -125,24 +126,30 @@ export default function BranchOverviewRenderer(props: BranchOverviewRendererProp </> )} <AnalysisStatus className="sw-mt-6" component={component} /> - <div className="sw-flex"> - <div className="sw-w-1/3 sw-mr-12 sw-pt-6"> - <QualityGatePanel - component={component} - loading={loadingStatus} - qgStatuses={qgStatuses} - qualityGate={qualityGate} + <div className="sw-flex sw-mt-6"> + <div className="sw-w-1/4 sw-mr-3"> + <Card className=" sw-h-max"> + <QualityGatePanel + component={component} + loading={loadingStatus} + qgStatuses={qgStatuses} + qualityGate={qualityGate} + /> + </Card> + <SonarLintPromotion + qgConditions={qgStatuses?.flatMap((qg) => qg.failedConditions)} /> </div> - <div className="sw-flex-1"> - <div className="sw-flex sw-flex-col sw-pt-6"> + <Card className="sw-flex-1 sw-pt-4"> + <div className="sw-flex sw-flex-col"> <TabsPanel analyses={analyses} appLeak={appLeak} component={component} loading={loadingStatus} period={period} + branch={branch} qgStatuses={qgStatuses} isNewCode={isNewCodeTab} onTabSelect={selectTab} @@ -185,7 +192,7 @@ export default function BranchOverviewRenderer(props: BranchOverviewRendererProp onGraphChange={onGraphChange} /> </div> - </div> + </Card> </div> </div> )} diff --git a/server/sonar-web/src/main/js/apps/overview/branches/QualityGatePanel.tsx b/server/sonar-web/src/main/js/apps/overview/branches/QualityGatePanel.tsx index e621fdc3b32..0118908120d 100644 --- a/server/sonar-web/src/main/js/apps/overview/branches/QualityGatePanel.tsx +++ b/server/sonar-web/src/main/js/apps/overview/branches/QualityGatePanel.tsx @@ -18,7 +18,6 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import { BasicSeparator, Card, Spinner } from 'design-system'; -import { flatMap } from 'lodash'; import * as React from 'react'; import { ComponentQualifier, isApplication } from '../../../types/component'; import { QualityGateStatus } from '../../../types/quality-gates'; @@ -27,7 +26,6 @@ import IgnoredConditionWarning from '../components/IgnoredConditionWarning'; import QualityGateStatusHeader from '../components/QualityGateStatusHeader'; import QualityGateStatusPassedView from '../components/QualityGateStatusPassedView'; import { QualityGateStatusTitle } from '../components/QualityGateStatusTitle'; -import SonarLintPromotion from '../components/SonarLintPromotion'; import ApplicationNonCaycProjectWarning from './ApplicationNonCaycProjectWarning'; import CleanAsYouCodeWarning from './CleanAsYouCodeWarning'; import QualityGatePanelSection from './QualityGatePanelSection'; @@ -71,41 +69,33 @@ export function QualityGatePanel(props: QualityGatePanelProps) { return ( <div data-test="overview__quality-gate-panel"> <QualityGateStatusTitle /> - <Card> - <div> - {loading ? ( - <div className="sw-p-6"> - <Spinner loading={loading} /> + <div className="sw-pt-5"> + <Spinner loading={loading}> + <QualityGateStatusHeader + status={overallLevel} + failedConditionCount={overallFailedConditionsCount} + /> + {success && <QualityGateStatusPassedView />} + + {showIgnoredConditionWarning && <IgnoredConditionWarning />} + + {!success && <BasicSeparator />} + + {overallFailedConditionsCount > 0 && ( + <div data-test="overview__quality-gate-conditions"> + {failedQgStatuses.map((qgStatus, qgStatusIdx) => ( + <QualityGatePanelSection + isApplication={isApp} + isLastStatus={qgStatusIdx === failedQgStatuses.length - 1} + key={qgStatus.key} + qgStatus={qgStatus} + qualityGate={qualityGate} + /> + ))} </div> - ) : ( - <> - <QualityGateStatusHeader - status={overallLevel} - failedConditionCount={overallFailedConditionsCount} - /> - {success && <QualityGateStatusPassedView />} - - {showIgnoredConditionWarning && <IgnoredConditionWarning />} - - {!success && <BasicSeparator />} - - {overallFailedConditionsCount > 0 && ( - <div data-test="overview__quality-gate-conditions"> - {failedQgStatuses.map((qgStatus, qgStatusIdx) => ( - <QualityGatePanelSection - isApplication={isApp} - isLastStatus={qgStatusIdx === failedQgStatuses.length - 1} - key={qgStatus.key} - qgStatus={qgStatus} - qualityGate={qualityGate} - /> - ))} - </div> - )} - </> )} - </div> - </Card> + </Spinner> + </div> {nonCaycProjectsInApp.length > 0 && ( <ApplicationNonCaycProjectWarning projects={nonCaycProjectsInApp} /> @@ -119,10 +109,6 @@ export function QualityGatePanel(props: QualityGatePanelProps) { <CleanAsYouCodeWarning component={component} /> </Card> )} - - <SonarLintPromotion - qgConditions={flatMap(qgStatuses, (qgStatus) => qgStatus.failedConditions)} - /> </div> ); } diff --git a/server/sonar-web/src/main/js/apps/overview/branches/TabsPanel.tsx b/server/sonar-web/src/main/js/apps/overview/branches/TabsPanel.tsx index 9756c817ae9..87a370b243b 100644 --- a/server/sonar-web/src/main/js/apps/overview/branches/TabsPanel.tsx +++ b/server/sonar-web/src/main/js/apps/overview/branches/TabsPanel.tsx @@ -20,6 +20,7 @@ import { isBefore, sub } from 'date-fns'; import { ButtonLink, + CardSeparator, FlagMessage, LightLabel, PageTitle, @@ -27,15 +28,17 @@ import { ToggleButton, } from 'design-system'; import * as React from 'react'; -import { FormattedMessage } from 'react-intl'; +import { FormattedMessage, useIntl } from 'react-intl'; import DocLink from '../../../components/common/DocLink'; -import { translate, translateWithParameters } from '../../../helpers/l10n'; +import { translate } from '../../../helpers/l10n'; import { isDiffMetric } from '../../../helpers/measures'; import { ApplicationPeriod } from '../../../types/application'; +import { Branch } from '../../../types/branch-like'; import { ComponentQualifier } from '../../../types/component'; import { Analysis, ProjectAnalysisEventCategory } from '../../../types/project-activity'; import { QualityGateStatus } from '../../../types/quality-gates'; import { Component, Period } from '../../../types/types'; +import LastAnalysisLabel from '../components/LastAnalysisLabel'; import { MeasuresTabs } from '../utils'; import { MAX_ANALYSES_NB } from './ActivityPanel'; import { LeakPeriodInfo } from './LeakPeriodInfo'; @@ -46,6 +49,7 @@ export interface MeasuresPanelProps { component: Component; loading?: boolean; period?: Period; + branch?: Branch; qgStatuses?: QualityGateStatus[]; isNewCode: boolean; onTabSelect: (tab: MeasuresTabs) => void; @@ -62,9 +66,10 @@ export function TabsPanel(props: React.PropsWithChildren<MeasuresPanelProps>) { period, qgStatuses = [], isNewCode, + branch, children, } = props; - + const intl = useIntl(); const isApp = component.qualifier === ComponentQualifier.Application; const leakPeriod = isApp ? appLeak : period; @@ -124,9 +129,11 @@ export function TabsPanel(props: React.PropsWithChildren<MeasuresPanelProps>) { return ( <div data-test="overview__measures-panel"> - <div className="sw-flex sw-mb-4"> + <div className="sw-flex sw-justify-between sw-items-center sw-mb-4"> <PageTitle as="h2" text={translate('overview.measures')} /> + <LastAnalysisLabel analysisDate={branch?.analysisDate} /> </div> + <CardSeparator className="sw--mx-6 sw-mb-3" /> {loading ? ( <div> @@ -160,9 +167,10 @@ export function TabsPanel(props: React.PropsWithChildren<MeasuresPanelProps>) { /> {failingConditions > 0 && ( <LightLabel className="sw-body-sm-highlight sw-ml-8"> - {failingConditions === 1 - ? translate('overview.1_condition_failed') - : translateWithParameters('overview.X_conditions_failed', failingConditions)} + {intl.formatMessage( + { id: 'overview.X_conditions_failed' }, + { conditions: failingConditions }, + )} </LightLabel> )} </div> 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 b06c4671c1e..75e12de4e5c 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 @@ -308,7 +308,7 @@ describe('project overview', () => { renderBranchOverview(); expect(await screen.findByText('metric.level.ERROR')).toBeInTheDocument(); - expect(screen.getAllByText('overview.X_conditions_failed.2')).toHaveLength(2); + expect(screen.getAllByText(/overview.X_conditions_failed/)).toHaveLength(2); }); it('should correctly show a project as empty', async () => { diff --git a/server/sonar-web/src/main/js/apps/overview/components/LastAnalysisLabel.tsx b/server/sonar-web/src/main/js/apps/overview/components/LastAnalysisLabel.tsx new file mode 100644 index 00000000000..5fcb961d0f3 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/overview/components/LastAnalysisLabel.tsx @@ -0,0 +1,44 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 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 React from 'react'; +import { useIntl } from 'react-intl'; +import DateFromNow from '../../../components/intl/DateFromNow'; + +interface Props { + analysisDate?: string; +} + +export default function LastAnalysisLabel({ analysisDate }: Readonly<Props>) { + const intl = useIntl(); + + return analysisDate ? ( + <span> + {intl.formatMessage( + { + id: 'overview.last_analysis_x', + }, + { + date: <DateFromNow className="sw-body-sm-highlight" date={analysisDate} />, + }, + )} + </span> + ) : null; +} diff --git a/server/sonar-web/src/main/js/apps/overview/components/QualityGateStatusHeader.tsx b/server/sonar-web/src/main/js/apps/overview/components/QualityGateStatusHeader.tsx index 7b0358799b2..c4eee2fd3ae 100644 --- a/server/sonar-web/src/main/js/apps/overview/components/QualityGateStatusHeader.tsx +++ b/server/sonar-web/src/main/js/apps/overview/components/QualityGateStatusHeader.tsx @@ -17,9 +17,10 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { QualityGateIndicator, TextError, TextMuted } from 'design-system'; +import { QualityGateIndicator, TextError } from 'design-system'; import React from 'react'; -import { translate, translateWithParameters } from '../../../helpers/l10n'; +import { useIntl } from 'react-intl'; +import { translate } from '../../../helpers/l10n'; import { Status } from '../../../types/types'; interface Props { @@ -29,26 +30,22 @@ interface Props { export default function QualityGateStatusHeader(props: Props) { const { status, failedConditionCount } = props; + const intl = useIntl(); return ( <div className="sw-flex sw-items-center sw-mb-4"> <QualityGateIndicator status={status} className="sw-mr-2" size="xl" /> <div className="sw-flex sw-flex-col"> - <div> - <TextMuted text={translate('overview.quality_gate')} /> - </div> - <div> - <span className="sw-heading-lg">{translate('metric.level', status)}</span> - </div> - </div> - <div className="sw-flex sw-flex-1 sw-justify-end"> + <span className="sw-heading-lg">{translate('metric.level', status)}</span> {failedConditionCount > 0 && ( <TextError - text={ - failedConditionCount === 1 - ? translate('overview.1_condition_failed') - : translateWithParameters('overview.X_conditions_failed', failedConditionCount) - } + className="sw-font-regular" + text={intl.formatMessage( + { id: 'overview.X_conditions_failed' }, + { + conditions: <strong>{failedConditionCount}</strong>, + }, + )} /> )} </div> diff --git a/server/sonar-web/src/main/js/apps/overview/components/QualityGateStatusTitle.tsx b/server/sonar-web/src/main/js/apps/overview/components/QualityGateStatusTitle.tsx index 57bc32771a0..4e2e543581c 100644 --- a/server/sonar-web/src/main/js/apps/overview/components/QualityGateStatusTitle.tsx +++ b/server/sonar-web/src/main/js/apps/overview/components/QualityGateStatusTitle.tsx @@ -17,23 +17,26 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { HelperHintIcon, PageTitle } from 'design-system'; +import { CardSeparator, HelperHintIcon, PageTitle } from 'design-system'; import React from 'react'; import HelpTooltip from '../../../components/controls/HelpTooltip'; import { translate } from '../../../helpers/l10n'; export function QualityGateStatusTitle() { return ( - <div className="sw-flex sw-items-center sw-mb-4"> - <div className="sw-flex sw-items-center"> - <PageTitle as="h2" text={translate('overview.quality_gate.status')} /> - <HelpTooltip - className="sw-ml-2" - overlay={<div className="sw-my-4">{translate('overview.quality_gate.help')}</div>} - > - <HelperHintIcon aria-label="help-tooltip" /> - </HelpTooltip> + <> + <div className="sw-flex sw-items-center sw-mb-4 sw--mt-2"> + <div className="sw-flex sw-items-center"> + <PageTitle as="h2" text={translate('overview.quality_gate.status')} /> + <HelpTooltip + className="sw-ml-2" + overlay={<div className="sw-my-4">{translate('overview.quality_gate.help')}</div>} + > + <HelperHintIcon aria-label="help-tooltip" /> + </HelpTooltip> + </div> </div> - </div> + <CardSeparator className="sw--mx-6" /> + </> ); } diff --git a/server/sonar-web/src/main/js/apps/overview/pullRequests/PullRequestMetaTopBar.tsx b/server/sonar-web/src/main/js/apps/overview/pullRequests/PullRequestMetaTopBar.tsx index 72c4a6a35cc..0ce4a8aae1f 100644 --- a/server/sonar-web/src/main/js/apps/overview/pullRequests/PullRequestMetaTopBar.tsx +++ b/server/sonar-web/src/main/js/apps/overview/pullRequests/PullRequestMetaTopBar.tsx @@ -21,12 +21,12 @@ import { SeparatorCircleIcon } from 'design-system'; import React from 'react'; import { useIntl } from 'react-intl'; import CurrentBranchLikeMergeInformation from '../../../app/components/nav/component/branch-like/CurrentBranchLikeMergeInformation'; -import DateFromNow from '../../../components/intl/DateFromNow'; import { getLeakValue } from '../../../components/measure/utils'; import { findMeasure, formatMeasure } from '../../../helpers/measures'; import { PullRequest } from '../../../types/branch-like'; import { MetricKey, MetricType } from '../../../types/metrics'; import { MeasureEnhanced } from '../../../types/types'; +import LastAnalysisLabel from '../components/LastAnalysisLabel'; interface Props { pullRequest: PullRequest; @@ -54,18 +54,7 @@ export default function PullRequestMetaTopBar({ pullRequest, measures }: Readonl {pullRequest.analysisDate && ( <> <SeparatorCircleIcon /> - {intl.formatMessage( - { - id: 'overview.last_analysis_x', - }, - { - date: ( - <strong className="sw-body-sm-highlight"> - <DateFromNow date={pullRequest.analysisDate} /> - </strong> - ), - }, - )} + <LastAnalysisLabel analysisDate={pullRequest.analysisDate} /> </> )} </div> 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 2d31837134b..c48bc626eda 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -3848,8 +3848,7 @@ system.version_is_availble={version} is available # OVERVIEW # #------------------------------------------------------------------------------ -overview.1_condition_failed=1 failed condition -overview.X_conditions_failed={0} failed conditions +overview.X_conditions_failed={conditions} {conditions, plural, one {failed condition} other {failed conditions}} overview.failed_condition.x_rating_required={rating} is {value}. Required {threshold} overview.failed_condition.x_required={metric}. Required {threshold} overview.fix_failed_conditions_with_sonarlint=Fix issues before they fail your Quality Gate with {link} in your IDE. Power up with connected mode! |