@@ -34,6 +34,7 @@ interface Props { | |||
metrics: { [key: string]: Metric }; | |||
onSonarCloud: boolean; | |||
project: string; | |||
qualifier: string; | |||
} | |||
interface State { | |||
@@ -66,7 +67,7 @@ export default class BadgesModal extends React.PureComponent<Props, State> { | |||
}; | |||
render() { | |||
const { branchLike, project } = this.props; | |||
const { branchLike, project, qualifier } = this.props; | |||
const { selectedType, badgeOptions } = this.state; | |||
const header = translate('overview.badges.title'); | |||
const fullBadgeOptions = { project, ...badgeOptions, ...getBranchLikeQuery(branchLike) }; | |||
@@ -76,7 +77,7 @@ export default class BadgesModal extends React.PureComponent<Props, State> { | |||
return ( | |||
<div className="overview-meta-card"> | |||
<Button className="js-project-badges" onClick={this.handleOpen}> | |||
{translate('overview.badges.get_badge')} | |||
{translate('overview.badges.get_badge', qualifier)} | |||
</Button> | |||
{this.state.open && ( | |||
<Modal contentLabel={header} onRequestClose={this.handleClose}> | |||
@@ -84,7 +85,9 @@ export default class BadgesModal extends React.PureComponent<Props, State> { | |||
<h2>{header}</h2> | |||
</header> | |||
<div className="modal-body"> | |||
<p className="huge-spacer-bottom">{translate('overview.badges.description')}</p> | |||
<p className="huge-spacer-bottom"> | |||
{translate('overview.badges.description', qualifier)} | |||
</p> | |||
<div className="badges-list spacer-bottom"> | |||
{badges.map(type => ( | |||
<BadgeButton | |||
@@ -97,7 +100,7 @@ export default class BadgesModal extends React.PureComponent<Props, State> { | |||
))} | |||
</div> | |||
<p className="text-center note huge-spacer-bottom"> | |||
{translate('overview.badges', selectedType, 'description')} | |||
{translate('overview.badges', selectedType, 'description', qualifier)} | |||
</p> | |||
<BadgeParams | |||
className="big-spacer-bottom" |
@@ -36,7 +36,13 @@ const shortBranch: ShortLivingBranch = { | |||
it('should display the modal after click on sonar cloud', () => { | |||
const wrapper = shallow( | |||
<BadgesModal branchLike={shortBranch} metrics={{}} onSonarCloud={true} project="foo" /> | |||
<BadgesModal | |||
branchLike={shortBranch} | |||
metrics={{}} | |||
onSonarCloud={true} | |||
project="foo" | |||
qualifier="TRK" | |||
/> | |||
); | |||
expect(wrapper).toMatchSnapshot(); | |||
click(wrapper.find('Button')); | |||
@@ -45,7 +51,13 @@ it('should display the modal after click on sonar cloud', () => { | |||
it('should display the modal after click on sonar qube', () => { | |||
const wrapper = shallow( | |||
<BadgesModal branchLike={shortBranch} metrics={{}} onSonarCloud={false} project="foo" /> | |||
<BadgesModal | |||
branchLike={shortBranch} | |||
metrics={{}} | |||
onSonarCloud={false} | |||
project="foo" | |||
qualifier="TRK" | |||
/> | |||
); | |||
expect(wrapper).toMatchSnapshot(); | |||
click(wrapper.find('Button')); |
@@ -8,7 +8,7 @@ exports[`should display the modal after click on sonar cloud 1`] = ` | |||
className="js-project-badges" | |||
onClick={[Function]} | |||
> | |||
overview.badges.get_badge | |||
overview.badges.get_badge.TRK | |||
</Button> | |||
</div> | |||
`; | |||
@@ -31,7 +31,7 @@ exports[`should display the modal after click on sonar cloud 2`] = ` | |||
<p | |||
className="huge-spacer-bottom" | |||
> | |||
overview.badges.description | |||
overview.badges.description.TRK | |||
</p> | |||
<div | |||
className="badges-list spacer-bottom" | |||
@@ -61,7 +61,7 @@ exports[`should display the modal after click on sonar cloud 2`] = ` | |||
<p | |||
className="text-center note huge-spacer-bottom" | |||
> | |||
overview.badges.measure.description | |||
overview.badges.measure.description.TRK | |||
</p> | |||
<BadgeParams | |||
className="big-spacer-bottom" | |||
@@ -101,7 +101,7 @@ exports[`should display the modal after click on sonar qube 1`] = ` | |||
className="js-project-badges" | |||
onClick={[Function]} | |||
> | |||
overview.badges.get_badge | |||
overview.badges.get_badge.TRK | |||
</Button> | |||
</div> | |||
`; | |||
@@ -124,7 +124,7 @@ exports[`should display the modal after click on sonar qube 2`] = ` | |||
<p | |||
className="huge-spacer-bottom" | |||
> | |||
overview.badges.description | |||
overview.badges.description.TRK | |||
</p> | |||
<div | |||
className="badges-list spacer-bottom" | |||
@@ -147,7 +147,7 @@ exports[`should display the modal after click on sonar qube 2`] = ` | |||
<p | |||
className="text-center note huge-spacer-bottom" | |||
> | |||
overview.badges.measure.description | |||
overview.badges.measure.description.TRK | |||
</p> | |||
<BadgeParams | |||
className="big-spacer-bottom" |
@@ -104,15 +104,15 @@ export default class Meta extends React.PureComponent<Props> { | |||
{organizationsEnabled && <MetaOrganizationKey organization={component.organization} />} | |||
</div> | |||
{isProject && | |||
!isPrivate && ( | |||
<BadgesModal | |||
branchLike={branchLike} | |||
metrics={metrics} | |||
onSonarCloud={onSonarCloud} | |||
project={component.key} | |||
/> | |||
)} | |||
{!isPrivate && ( | |||
<BadgesModal | |||
branchLike={branchLike} | |||
metrics={metrics} | |||
onSonarCloud={onSonarCloud} | |||
project={component.key} | |||
qualifier={component.qualifier} | |||
/> | |||
)} | |||
</div> | |||
); | |||
} |
@@ -34,11 +34,11 @@ import { getChildren } from '../../../api/components'; | |||
import { translate } from '../../../helpers/l10n'; | |||
import { fetchMetrics } from '../../../store/rootActions'; | |||
import { getMetrics } from '../../../store/rootReducer'; | |||
import { Metric } from '../../../app/types'; | |||
import { Metric, Component } from '../../../app/types'; | |||
import '../styles.css'; | |||
interface OwnProps { | |||
component: { key: string; name: string }; | |||
component: Component; | |||
} | |||
interface StateToProps { | |||
@@ -184,10 +184,21 @@ export class App extends React.PureComponent<Props, State> { | |||
<div className="page-main">{this.renderMain()}</div> | |||
<aside className="page-sidebar-fixed"> | |||
{!this.isEmpty() && | |||
!this.isNotComputed() && <Summary component={component} measures={measures!} />} | |||
<Activity component={component.key} metrics={this.props.metrics} /> | |||
<Report component={component} /> | |||
<div className="portfolio-meta-card"> | |||
<h4 className="portfolio-meta-header"> | |||
{translate('overview.about_this_portfolio')} | |||
</h4> | |||
{!this.isEmpty() && | |||
!this.isNotComputed() && <Summary component={component} measures={measures!} />} | |||
</div> | |||
<div className="portfolio-meta-card"> | |||
<Activity component={component.key} metrics={this.props.metrics} /> | |||
</div> | |||
<div className="portfolio-meta-card"> | |||
<Report component={component} /> | |||
</div> | |||
</aside> | |||
</div> | |||
</div> |
@@ -34,7 +34,7 @@ export default function Summary({ component, measures }: Props) { | |||
const nclocDistribution = measures['ncloc_language_distribution']; | |||
return ( | |||
<section id="portfolio-summary" className="big-spacer-bottom"> | |||
<section className="big-spacer-bottom" id="portfolio-summary"> | |||
{component.description && <div className="big-spacer-bottom">{component.description}</div>} | |||
<ul className="portfolio-grid"> |
@@ -44,11 +44,12 @@ jest.mock('../Report', () => ({ | |||
import * as React from 'react'; | |||
import { shallow, mount } from 'enzyme'; | |||
import { App } from '../App'; | |||
import { Component } from '../../../../app/types'; | |||
const getMeasures = require('../../../../api/measures').getMeasures as jest.Mock<any>; | |||
const getChildren = require('../../../../api/components').getChildren as jest.Mock<any>; | |||
const component = { key: 'foo', name: 'Foo' }; | |||
const component = { key: 'foo', name: 'Foo', qualifier: 'TRK' } as Component; | |||
it('renders', () => { | |||
const wrapper = shallow(<App component={component} fetchMetrics={jest.fn()} metrics={{}} />); |
@@ -61,32 +61,51 @@ exports[`renders 1`] = ` | |||
<aside | |||
className="page-sidebar-fixed" | |||
> | |||
<Summary | |||
component={ | |||
Object { | |||
"key": "foo", | |||
"name": "Foo", | |||
<div | |||
className="portfolio-meta-card" | |||
> | |||
<h4 | |||
className="portfolio-meta-header" | |||
> | |||
overview.about_this_portfolio | |||
</h4> | |||
<Summary | |||
component={ | |||
Object { | |||
"key": "foo", | |||
"name": "Foo", | |||
"qualifier": "TRK", | |||
} | |||
} | |||
} | |||
measures={ | |||
Object { | |||
"ncloc": "173", | |||
"reliability_rating": "1", | |||
measures={ | |||
Object { | |||
"ncloc": "173", | |||
"reliability_rating": "1", | |||
} | |||
} | |||
} | |||
/> | |||
<Activity | |||
component="foo" | |||
metrics={Object {}} | |||
/> | |||
<Report | |||
component={ | |||
Object { | |||
"key": "foo", | |||
"name": "Foo", | |||
/> | |||
</div> | |||
<div | |||
className="portfolio-meta-card" | |||
> | |||
<Activity | |||
component="foo" | |||
metrics={Object {}} | |||
/> | |||
</div> | |||
<div | |||
className="portfolio-meta-card" | |||
> | |||
<Report | |||
component={ | |||
Object { | |||
"key": "foo", | |||
"name": "Foo", | |||
"qualifier": "TRK", | |||
} | |||
} | |||
} | |||
/> | |||
/> | |||
</div> | |||
</aside> | |||
</div> | |||
</div> | |||
@@ -116,18 +135,36 @@ exports[`renders when portfolio is empty 1`] = ` | |||
<aside | |||
className="page-sidebar-fixed" | |||
> | |||
<Activity | |||
component="foo" | |||
metrics={Object {}} | |||
/> | |||
<Report | |||
component={ | |||
Object { | |||
"key": "foo", | |||
"name": "Foo", | |||
<div | |||
className="portfolio-meta-card" | |||
> | |||
<h4 | |||
className="portfolio-meta-header" | |||
> | |||
overview.about_this_portfolio | |||
</h4> | |||
</div> | |||
<div | |||
className="portfolio-meta-card" | |||
> | |||
<Activity | |||
component="foo" | |||
metrics={Object {}} | |||
/> | |||
</div> | |||
<div | |||
className="portfolio-meta-card" | |||
> | |||
<Report | |||
component={ | |||
Object { | |||
"key": "foo", | |||
"name": "Foo", | |||
"qualifier": "TRK", | |||
} | |||
} | |||
} | |||
/> | |||
/> | |||
</div> | |||
</aside> | |||
</div> | |||
</div> | |||
@@ -154,18 +191,36 @@ exports[`renders when portfolio is not computed 1`] = ` | |||
<aside | |||
className="page-sidebar-fixed" | |||
> | |||
<Activity | |||
component="foo" | |||
metrics={Object {}} | |||
/> | |||
<Report | |||
component={ | |||
Object { | |||
"key": "foo", | |||
"name": "Foo", | |||
<div | |||
className="portfolio-meta-card" | |||
> | |||
<h4 | |||
className="portfolio-meta-header" | |||
> | |||
overview.about_this_portfolio | |||
</h4> | |||
</div> | |||
<div | |||
className="portfolio-meta-card" | |||
> | |||
<Activity | |||
component="foo" | |||
metrics={Object {}} | |||
/> | |||
</div> | |||
<div | |||
className="portfolio-meta-card" | |||
> | |||
<Report | |||
component={ | |||
Object { | |||
"key": "foo", | |||
"name": "Foo", | |||
"qualifier": "TRK", | |||
} | |||
} | |||
} | |||
/> | |||
/> | |||
</div> | |||
</aside> | |||
</div> | |||
</div> |
@@ -110,3 +110,19 @@ | |||
.portfolio-sub-components-cell { | |||
width: 90px; | |||
} | |||
.portfolio-meta-header { | |||
margin-bottom: calc(0.5 * var(--gridSize)); | |||
color: var(--baseFontColor); | |||
} | |||
.portfolio-meta-card { | |||
min-width: 200px; | |||
box-sizing: border-box; | |||
} | |||
.portfolio-meta-card + .portfolio-meta-card { | |||
margin-top: calc(2 * var(--gridSize)); | |||
padding-top: calc(2 * var(--gridSize) - 1px); | |||
border-top: 1px solid var(--barBorderColor); | |||
} |
@@ -36,6 +36,7 @@ jest.mock('../../../app/utils/handleRequiredAuthorization', () => ({ | |||
import * as React from 'react'; | |||
import { mount } from 'enzyme'; | |||
import App from '../App'; | |||
import { Component } from '../../../app/types'; | |||
const associateGateWithProject = require('../../../api/quality-gates') | |||
.associateGateWithProject as jest.Mock<any>; | |||
@@ -62,7 +63,7 @@ const component = { | |||
organization: 'org', | |||
qualifier: 'TRK', | |||
version: '0.0.1' | |||
}; | |||
} as Component; | |||
beforeEach(() => { | |||
associateGateWithProject.mockClear(); | |||
@@ -73,7 +74,10 @@ beforeEach(() => { | |||
it('checks permissions', () => { | |||
handleRequiredAuthorization.mockClear(); | |||
mount( | |||
<App component={{ ...component, configuration: undefined }} onComponentChange={jest.fn()} /> | |||
<App | |||
component={{ ...component, configuration: undefined } as Component} | |||
onComponentChange={jest.fn()} | |||
/> | |||
); | |||
expect(handleRequiredAuthorization).toBeCalled(); | |||
}); |
@@ -2297,6 +2297,7 @@ overview.last_analysis_x=last analysis {0} | |||
overview.started_on_x=Started on {0} | |||
overview.last_analysis_on_x=Last analysis on {0} | |||
overview.on_new_code=On New Code | |||
overview.about_this_portfolio=About This Portfolio | |||
overview.about_this_project.APP=About This Application | |||
overview.about_this_project.TRK=About This Project | |||
overview.about_this_project.BRC=About This Sub-Project | |||
@@ -2356,15 +2357,22 @@ overview.complexity_tooltip.file={0} files have complexity around {1} | |||
overview.deprecated_profile=This quality profile uses {0} deprecated rules and should be updated. | |||
overview.badges.get_badge=Get project badges | |||
overview.badges.get_badge.TRK=Get project badges | |||
overview.badges.get_badge.VW=Get portfolio badges | |||
overview.badges.get_badge.APP=Get application badges | |||
overview.badges.title=Badges | |||
overview.badges.description=Show the status of your project metrics on your README or website. Pick your style: | |||
overview.badges.description.TRK=Show the status of your project metrics on your README or website. Pick your style: | |||
overview.badges.description.VW=Show the status of your portfolio metrics on your README or website. Pick your style: | |||
overview.badges.description.APP=Show the status of your application metrics on your README or website. Pick your style: | |||
overview.badges.metric=Metric | |||
overview.badges.options.colors.white=White | |||
overview.badges.options.colors.black=Black | |||
overview.badges.options.colors.orange=Orange | |||
overview.badges.measure.alt=Standard badge | |||
overview.badges.measure.description=This badge dynamically displays the current status of one metric of your project. | |||
overview.badges.measure.description.TRK=This badge dynamically displays the current status of one metric of your project. | |||
overview.badges.measure.description.VW=This badge dynamically displays the current status of one metric of your portfolio. | |||
overview.badges.measure.description.APP=This badge dynamically displays the current status of one metric of your application. | |||
overview.badges.marketing.alt=Scanned on SonarCloud badge | |||
overview.badges.marketing.description=This badge lets you advertise that you're using SonarCloud for code quality. | |||
overview.badges.quality_gate.alt=SonarCloud Quality Gate badge |