Browse Source

GOV-288 Portfolio containing only provisioned projects is not 'empty'

tags/7.5
Pascal Mugnier 6 years ago
parent
commit
b9d7623d0c

+ 13
- 2
server/sonar-web/src/main/js/apps/issues/components/App.tsx View File

@@ -67,6 +67,7 @@ import {
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { RawQuery } from '../../../helpers/query';
import { scrollToElement } from '../../../helpers/scrolling';
import EmptySearch from '../../../components/common/EmptySearch';
import Checkbox from '../../../components/controls/Checkbox';

import '../styles.css';
@@ -926,6 +927,17 @@ export default class App extends React.PureComponent<Props, State> {
return null;
}

let noIssuesMessage = null;
if (paging.total === 0) {
if (this.isFiltered()) {
noIssuesMessage = <EmptySearch />;
} else if (this.state.myIssues) {
noIssuesMessage = <NoMyIssues />;
} else {
noIssuesMessage = <NoIssues />;
}
}

return (
<div>
{paging.total > 0 && (
@@ -949,8 +961,7 @@ export default class App extends React.PureComponent<Props, State> {
<ListFooter count={issues.length} loadMore={this.fetchMoreIssues} total={paging.total} />
)}

{paging.total === 0 &&
(this.state.myIssues && !this.isFiltered() ? <NoMyIssues /> : <NoIssues />)}
{noIssuesMessage}
</div>
);
}

+ 51
- 21
server/sonar-web/src/main/js/apps/overview/components/OverviewApp.tsx View File

@@ -45,6 +45,7 @@ import { isSameBranchLike, getBranchLikeQuery } from '../../../helpers/branches'
import { fetchMetrics } from '../../../store/rootActions';
import { getMetrics } from '../../../store/rootReducer';
import { BranchLike, Component, Metric } from '../../../app/types';
import { translate } from '../../../helpers/l10n';
import '../styles.css';

interface OwnProps {
@@ -158,6 +159,10 @@ export class OverviewApp extends React.PureComponent<Props, State> {
? { index: 1 }
: undefined;

isEmpty = () =>
this.state.measures === undefined ||
this.state.measures.find(measure => measure.metric.key === 'ncloc') === undefined;

renderLoading() {
return (
<div className="text-center">
@@ -166,14 +171,22 @@ export class OverviewApp extends React.PureComponent<Props, State> {
);
}

render() {
const { branchLike, component } = this.props;
const { loading, measures, periods, history, historyStartDate } = this.state;

if (loading) {
return this.renderLoading();
}
renderEmpty() {
return (
<div className="overview-main page-main">
<h3>
{!this.state.measures ||
!this.state.measures.find(measure => measure.metric.key === 'projects')
? translate('portfolio.app.empty')
: translate('portfolio.app.no_lines_of_code')}
</h3>
</div>
);
}

renderMain() {
const { branchLike, component } = this.props;
const { periods, measures, history, historyStartDate } = this.state;
const leakPeriod =
component.qualifier === 'APP' ? this.getApplicationLeakPeriod() : getLeakPeriod(periods);
const domainProps = {
@@ -185,23 +198,40 @@ export class OverviewApp extends React.PureComponent<Props, State> {
historyStartDate
};

if (this.isEmpty()) {
return this.renderEmpty();
}

return (
<div className="overview-main page-main">
{component.qualifier === 'APP' ? (
<ApplicationQualityGate component={component} />
) : (
<QualityGate branchLike={branchLike} component={component} measures={measures} />
)}

<div className="overview-domains-list">
<BugsAndVulnerabilities {...domainProps} />
<CodeSmells {...domainProps} />
<Coverage {...domainProps} />
<Duplications {...domainProps} />
</div>
</div>
);
}

render() {
const { branchLike, component } = this.props;
const { loading, measures, history } = this.state;

if (loading) {
return this.renderLoading();
}

return (
<div className="page page-limited">
<div className="overview page-with-sidebar">
<div className="overview-main page-main">
{component.qualifier === 'APP' ? (
<ApplicationQualityGate component={component} />
) : (
<QualityGate branchLike={branchLike} component={component} measures={measures} />
)}

<div className="overview-domains-list">
<BugsAndVulnerabilities {...domainProps} />
<CodeSmells {...domainProps} />
<Coverage {...domainProps} />
<Duplications {...domainProps} />
</div>
</div>
{this.renderMain()}

<div className="overview-sidebar page-sidebar-fixed">
<Meta

+ 32
- 23
server/sonar-web/src/main/js/apps/overview/meta/MetaSize.tsx View File

@@ -34,21 +34,27 @@ interface Props {
}

export default class MetaSize extends React.PureComponent<Props> {
renderLoC = (ncloc: MeasureEnhanced) => (
renderLoC = (ncloc?: MeasureEnhanced) => (
<div
id="overview-ncloc"
className={classNames('overview-meta-size-ncloc', {
'is-half-width': this.props.component.qualifier === 'APP'
})}>
<span className="spacer-right">
<SizeRating value={Number(ncloc.value)} />
</span>
<DrilldownLink
branchLike={this.props.branchLike}
component={this.props.component.key}
metric="ncloc">
{formatMeasure(ncloc.value, 'SHORT_INT')}
</DrilldownLink>
})}
id="overview-ncloc">
{ncloc && (
<span className="spacer-right">
<SizeRating value={Number(ncloc.value)} />
</span>
)}
{ncloc ? (
<DrilldownLink
branchLike={this.props.branchLike}
component={this.props.component.key}
metric="ncloc">
{formatMeasure(ncloc.value, 'SHORT_INT')}
</DrilldownLink>
) : (
<span>0</span>
)}
<div className="spacer-top text-muted">{getMetricName('ncloc')}</div>
</div>
);
@@ -70,24 +76,27 @@ export default class MetaSize extends React.PureComponent<Props> {

renderProjects = () => {
const projects = this.props.measures.find(measure => measure.metric.key === 'projects');

return projects ? (
<div id="overview-projects" className="overview-meta-size-ncloc is-half-width">
<DrilldownLink
branchLike={this.props.branchLike}
component={this.props.component.key}
metric="projects">
{formatMeasure(projects.value, 'SHORT_INT')}
</DrilldownLink>
return (
<div className="overview-meta-size-ncloc is-half-width" id="overview-projects">
{projects ? (
<DrilldownLink
branchLike={this.props.branchLike}
component={this.props.component.key}
metric="projects">
{formatMeasure(projects.value, 'SHORT_INT')}
</DrilldownLink>
) : (
<span>0</span>
)}
<div className="spacer-top text-muted">{translate('metric.projects.name')}</div>
</div>
) : null;
);
};

render() {
const ncloc = this.props.measures.find(measure => measure.metric.key === 'ncloc');

if (ncloc == null) {
if (ncloc == null && this.props.component.qualifier !== 'APP') {
return null;
}


+ 2
- 1
server/sonar-web/src/main/js/apps/overview/styles.css View File

@@ -363,7 +363,8 @@
box-sizing: border-box;
}

.overview-meta-size-ncloc a {
.overview-meta-size-ncloc a,
.overview-meta-size-ncloc span {
line-height: var(--controlHeight);
font-size: 18px;
font-weight: 300;

+ 6
- 4
server/sonar-web/src/main/js/apps/portfolio/components/App.tsx View File

@@ -123,8 +123,11 @@ export class App extends React.PureComponent<Props, State> {
renderEmpty() {
return (
<div className="empty-search">
<h3>{translate('portfolio.empty')}</h3>
<p>{translate('portfolio.empty.hint')}</p>
<h3>
{!this.state.measures || !this.state.measures['projects']
? translate('portfolio.empty')
: translate('portfolio.no_lines_of_code')}
</h3>
</div>
);
}
@@ -188,8 +191,7 @@ export class App extends React.PureComponent<Props, State> {
<h4 className="portfolio-meta-header">
{translate('overview.about_this_portfolio')}
</h4>
{!this.isEmpty() &&
!this.isNotComputed() && <Summary component={component} measures={measures!} />}
<Summary component={component} measures={measures || {}} />
</div>

<div className="portfolio-meta-card">

+ 5
- 3
server/sonar-web/src/main/js/apps/portfolio/components/RatingFreshness.tsx View File

@@ -33,9 +33,11 @@ export default function RatingFreshness({ lastChange, rating }: Props) {
return (
<div className="portfolio-freshness">
{rating && (
<>
{translate('portfolio.has_always_been')} <Rating small={true} value={rating} />
</>
<FormattedMessage
defaultMessage={translate('portfolio.has_always_been_x')}
id="portfolio.has_always_been_x"
values={{ rating: <Rating small={true} value={rating} /> }}
/>
)}
</div>
);

+ 14
- 6
server/sonar-web/src/main/js/apps/portfolio/components/Summary.tsx View File

@@ -40,17 +40,25 @@ export default function Summary({ component, measures }: Props) {
<ul className="portfolio-grid">
<li>
<div className="portfolio-measure-secondary-value">
<Link to={getComponentDrilldownUrl(component.key, 'projects')}>
<Measure metricKey="projects" metricType="SHORT_INT" value={projects} />
</Link>
{projects ? (
<Link to={getComponentDrilldownUrl(component.key, 'projects')}>
<Measure metricKey="projects" metricType="SHORT_INT" value={projects} />
</Link>
) : (
'0'
)}
</div>
<div className="spacer-top text-muted">{translate('projects')}</div>
</li>
<li>
<div className="portfolio-measure-secondary-value">
<Link to={getComponentDrilldownUrl(component.key, 'ncloc')}>
<Measure metricKey="ncloc" metricType="SHORT_INT" value={ncloc} />
</Link>
{ncloc ? (
<Link to={getComponentDrilldownUrl(component.key, 'ncloc')}>
<Measure metricKey="ncloc" metricType="SHORT_INT" value={ncloc} />
</Link>
) : (
'0'
)}
</div>
<div className="spacer-top text-muted">{translate('metric.ncloc.name')}</div>
</li>

+ 28
- 3
server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/App-test.tsx.snap View File

@@ -127,9 +127,6 @@ exports[`renders when portfolio is empty 1`] = `
<h3>
portfolio.empty
</h3>
<p>
portfolio.empty.hint
</p>
</div>
</div>
<aside
@@ -143,6 +140,20 @@ exports[`renders when portfolio is empty 1`] = `
>
overview.about_this_portfolio
</h4>
<Summary
component={
Object {
"key": "foo",
"name": "Foo",
"qualifier": "TRK",
}
}
measures={
Object {
"reliability_rating": "1",
}
}
/>
</div>
<div
className="portfolio-meta-card"
@@ -199,6 +210,20 @@ exports[`renders when portfolio is not computed 1`] = `
>
overview.about_this_portfolio
</h4>
<Summary
component={
Object {
"key": "foo",
"name": "Foo",
"qualifier": "TRK",
}
}
measures={
Object {
"ncloc": "173",
}
}
/>
</div>
<div
className="portfolio-meta-card"

+ 12
- 8
server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/RatingFreshness-test.tsx.snap View File

@@ -32,13 +32,17 @@ exports[`renders has always been 1`] = `
<div
className="portfolio-freshness"
>
<React.Fragment>
portfolio.has_always_been
<Rating
small={true}
value="A"
/>
</React.Fragment>
<FormattedMessage
defaultMessage="portfolio.has_always_been_x"
id="portfolio.has_always_been_x"
values={
Object {
"rating": <Rating
small={true}
value="A"
/>,
}
}
/>
</div>
`;

+ 4
- 2
sonar-core/src/main/resources/org/sonar/l10n/core.properties View File

@@ -2678,12 +2678,14 @@ branches.see_the_pr=See the PR
# PORTFOLIOS
#
#------------------------------------------------------------------------------
portfolio.has_always_been=has always been
portfolio.has_always_been_x=has always been {rating}
portfolio.was_x_y=was {rating} {date}
portfolio.x_in_y={projects} in {rating}
portfolio.empty=This portfolio is empty.
portfolio.empty.hint=This portfolio has no projects, or none of associated projects has lines of code.
portfolio.no_lines_of_code=All projects in this portfolio are empty
portfolio.not_computed=This portfolio is not yet computed.
portfolio.app.empty=This application is empty.
portfolio.app.no_lines_of_code=All projects in this application are empty


#------------------------------------------------------------------------------

Loading…
Cancel
Save