@@ -97,7 +97,7 @@ export default class Activity extends React.PureComponent<Props> { | |||
render() { | |||
return ( | |||
<div className="huge-spacer-top"> | |||
<div className="huge-spacer-bottom"> | |||
<header className="page-header"> | |||
<h3 className="page-title">{translate('project_activity.page')}</h3> | |||
</header> |
@@ -31,6 +31,7 @@ import { getChildren } from '../../../api/components'; | |||
import { PORTFOLIO_METRICS, SUB_COMPONENTS_METRICS, convertMeasures } from '../utils'; | |||
import { SubComponent } from '../types'; | |||
import '../styles.css'; | |||
import { translate } from '../../../helpers/l10n'; | |||
interface Props { | |||
component: { key: string; name: string }; | |||
@@ -97,6 +98,11 @@ export default class App extends React.PureComponent<Props, State> { | |||
); | |||
} | |||
isEmpty = () => this.state.measures == undefined || this.state.measures['ncloc'] == undefined; | |||
isNotComputed = () => | |||
this.state.measures && this.state.measures['reliability_rating'] == undefined; | |||
renderSpinner() { | |||
return ( | |||
<div className="page page-limited"> | |||
@@ -107,9 +113,59 @@ export default class App extends React.PureComponent<Props, State> { | |||
); | |||
} | |||
renderEmpty() { | |||
return ( | |||
<div className="empty-search"> | |||
<h3>{translate('portfolio.empty')}</h3> | |||
<p>{translate('portfolio.empty.hint')}</p> | |||
</div> | |||
); | |||
} | |||
renderWhenNotComputed() { | |||
return ( | |||
<div className="empty-search"> | |||
<h3>{translate('portfolio.not_computed')}</h3> | |||
</div> | |||
); | |||
} | |||
renderMain() { | |||
const { component } = this.props; | |||
const { measures, subComponents, totalSubComponents } = this.state; | |||
if (this.isEmpty()) { | |||
return this.renderEmpty(); | |||
} | |||
if (this.isNotComputed()) { | |||
return this.renderWhenNotComputed(); | |||
} | |||
return ( | |||
<div> | |||
<div className="portfolio-boxes"> | |||
<ReleasabilityBox component={component.key} measures={measures!} /> | |||
<ReliabilityBox component={component.key} measures={measures!} /> | |||
<SecurityBox component={component.key} measures={measures!} /> | |||
<MaintainabilityBox component={component.key} measures={measures!} /> | |||
</div> | |||
{subComponents != undefined && | |||
totalSubComponents != undefined && ( | |||
<WorstProjects | |||
component={component.key} | |||
subComponents={subComponents} | |||
total={totalSubComponents} | |||
/> | |||
)} | |||
</div> | |||
); | |||
} | |||
render() { | |||
const { component } = this.props; | |||
const { loading, measures, subComponents, totalSubComponents } = this.state; | |||
const { loading, measures } = this.state; | |||
if (loading) { | |||
return this.renderSpinner(); | |||
@@ -118,28 +174,11 @@ export default class App extends React.PureComponent<Props, State> { | |||
return ( | |||
<div className="page page-limited"> | |||
<div className="page-with-sidebar"> | |||
<div className="page-main"> | |||
{measures != undefined && ( | |||
<div className="portfolio-boxes"> | |||
<ReleasabilityBox component={component.key} measures={measures} /> | |||
<ReliabilityBox component={component.key} measures={measures} /> | |||
<SecurityBox component={component.key} measures={measures} /> | |||
<MaintainabilityBox component={component.key} measures={measures} /> | |||
</div> | |||
)} | |||
{subComponents != undefined && | |||
totalSubComponents != undefined && ( | |||
<WorstProjects | |||
component={component.key} | |||
subComponents={subComponents} | |||
total={totalSubComponents} | |||
/> | |||
)} | |||
</div> | |||
<div className="page-main">{this.renderMain()}</div> | |||
<aside className="page-sidebar-fixed"> | |||
{measures != undefined && <Summary component={component} measures={measures} />} | |||
{!this.isEmpty() && | |||
!this.isNotComputed() && <Summary component={component} measures={measures!} />} | |||
<Activity component={component.key} /> | |||
<Report component={component} /> | |||
</aside> |
@@ -71,7 +71,7 @@ export default class Report extends React.PureComponent<Props, State> { | |||
if (loading) { | |||
return ( | |||
<div className="huge-spacer-top"> | |||
<div> | |||
{this.renderHeader()} | |||
<i className="spinner" /> | |||
</div> | |||
@@ -83,7 +83,7 @@ export default class Report extends React.PureComponent<Props, State> { | |||
} | |||
return ( | |||
<div className="huge-spacer-top"> | |||
<div> | |||
{this.renderHeader()} | |||
{!status.canDownload && ( |
@@ -35,7 +35,7 @@ export default function Summary({ component, measures }: Props) { | |||
const nclocDistribution = measures['ncloc_language_distribution']; | |||
return ( | |||
<section id="portfolio-summary" className="portfolio-section portfolio-section-summary"> | |||
<section id="portfolio-summary" className="huge-spacer-bottom"> | |||
{component.description && <div className="big-spacer-bottom">{component.description}</div>} | |||
<ul className="portfolio-grid"> |
@@ -49,7 +49,24 @@ const component = { key: 'foo', name: 'Foo' }; | |||
it('renders', () => { | |||
const wrapper = shallow(<App component={component} />); | |||
wrapper.setState({ loading: false, measures: {}, subComponents: [], totalSubComponents: 0 }); | |||
wrapper.setState({ | |||
loading: false, | |||
measures: { ncloc: '173', reliability_rating: '1' }, | |||
subComponents: [], | |||
totalSubComponents: 0 | |||
}); | |||
expect(wrapper).toMatchSnapshot(); | |||
}); | |||
it('renders when portfolio is empty', () => { | |||
const wrapper = shallow(<App component={component} />); | |||
wrapper.setState({ loading: false, measures: { reliability_rating: '1' } }); | |||
expect(wrapper).toMatchSnapshot(); | |||
}); | |||
it('renders when portfolio is not computed', () => { | |||
const wrapper = shallow(<App component={component} />); | |||
wrapper.setState({ loading: false, measures: { ncloc: '173' } }); | |||
expect(wrapper).toMatchSnapshot(); | |||
}); | |||
@@ -2,7 +2,7 @@ | |||
exports[`renders 1`] = ` | |||
<div | |||
className="huge-spacer-top" | |||
className="huge-spacer-bottom" | |||
> | |||
<header | |||
className="page-header" |
@@ -10,44 +10,148 @@ exports[`renders 1`] = ` | |||
<div | |||
className="page-main" | |||
> | |||
<div | |||
className="portfolio-boxes" | |||
> | |||
<ReleasabilityBox | |||
component="foo" | |||
measures={Object {}} | |||
/> | |||
<ReliabilityBox | |||
component="foo" | |||
measures={Object {}} | |||
/> | |||
<SecurityBox | |||
component="foo" | |||
measures={Object {}} | |||
/> | |||
<MaintainabilityBox | |||
<div> | |||
<div | |||
className="portfolio-boxes" | |||
> | |||
<ReleasabilityBox | |||
component="foo" | |||
measures={ | |||
Object { | |||
"ncloc": "173", | |||
"reliability_rating": "1", | |||
} | |||
} | |||
/> | |||
<ReliabilityBox | |||
component="foo" | |||
measures={ | |||
Object { | |||
"ncloc": "173", | |||
"reliability_rating": "1", | |||
} | |||
} | |||
/> | |||
<SecurityBox | |||
component="foo" | |||
measures={ | |||
Object { | |||
"ncloc": "173", | |||
"reliability_rating": "1", | |||
} | |||
} | |||
/> | |||
<MaintainabilityBox | |||
component="foo" | |||
measures={ | |||
Object { | |||
"ncloc": "173", | |||
"reliability_rating": "1", | |||
} | |||
} | |||
/> | |||
</div> | |||
<WorstProjects | |||
component="foo" | |||
measures={Object {}} | |||
subComponents={Array []} | |||
total={0} | |||
/> | |||
</div> | |||
<WorstProjects | |||
</div> | |||
<aside | |||
className="page-sidebar-fixed" | |||
> | |||
<Summary | |||
component={ | |||
Object { | |||
"key": "foo", | |||
"name": "Foo", | |||
} | |||
} | |||
measures={ | |||
Object { | |||
"ncloc": "173", | |||
"reliability_rating": "1", | |||
} | |||
} | |||
/> | |||
<Activity | |||
component="foo" | |||
subComponents={Array []} | |||
total={0} | |||
/> | |||
<Report | |||
component={ | |||
Object { | |||
"key": "foo", | |||
"name": "Foo", | |||
} | |||
} | |||
/> | |||
</aside> | |||
</div> | |||
</div> | |||
`; | |||
exports[`renders when portfolio is empty 1`] = ` | |||
<div | |||
className="page page-limited" | |||
> | |||
<div | |||
className="page-with-sidebar" | |||
> | |||
<div | |||
className="page-main" | |||
> | |||
<div | |||
className="empty-search" | |||
> | |||
<h3> | |||
portfolio.empty | |||
</h3> | |||
<p> | |||
portfolio.empty.hint | |||
</p> | |||
</div> | |||
</div> | |||
<aside | |||
className="page-sidebar-fixed" | |||
> | |||
<Summary | |||
<Activity | |||
component="foo" | |||
/> | |||
<Report | |||
component={ | |||
Object { | |||
"key": "foo", | |||
"name": "Foo", | |||
} | |||
} | |||
measures={Object {}} | |||
/> | |||
</aside> | |||
</div> | |||
</div> | |||
`; | |||
exports[`renders when portfolio is not computed 1`] = ` | |||
<div | |||
className="page page-limited" | |||
> | |||
<div | |||
className="page-with-sidebar" | |||
> | |||
<div | |||
className="page-main" | |||
> | |||
<div | |||
className="empty-search" | |||
> | |||
<h3> | |||
portfolio.not_computed | |||
</h3> | |||
</div> | |||
</div> | |||
<aside | |||
className="page-sidebar-fixed" | |||
> | |||
<Activity | |||
component="foo" | |||
/> |
@@ -1,9 +1,7 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`renders 1`] = ` | |||
<div | |||
className="huge-spacer-top" | |||
> | |||
<div> | |||
<header | |||
className="page-header" | |||
> | |||
@@ -20,9 +18,7 @@ exports[`renders 1`] = ` | |||
`; | |||
exports[`renders 2`] = ` | |||
<div | |||
className="huge-spacer-top" | |||
> | |||
<div> | |||
<header | |||
className="page-header" | |||
> |
@@ -2,7 +2,7 @@ | |||
exports[`renders 1`] = ` | |||
<section | |||
className="portfolio-section portfolio-section-summary" | |||
className="huge-spacer-bottom" | |||
id="portfolio-summary" | |||
> | |||
<div |
@@ -83,6 +83,9 @@ th.nowrap { | |||
.huge-spacer-top { | |||
margin-top: 40px; | |||
} | |||
.huge-spacer-bottom { | |||
margin-bottom: 40px; | |||
} | |||
.huge-spacer-left { | |||
margin-left: 40px; | |||
} |
@@ -3194,3 +3194,6 @@ branches.settings_hint_tab=Administration > Branches | |||
#------------------------------------------------------------------------------ | |||
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.not_computed=This portfolio is not yet computed. |