@@ -35,6 +35,7 @@ import { isDiffMetric, getPeriodValue } from '../../../helpers/measures'; | |||
import { RequestData } from '../../../helpers/request'; | |||
import { getProjectUrl } from '../../../helpers/urls'; | |||
import { getMeasures } from '../../../api/measures'; | |||
import { translate } from '../../../helpers/l10n'; | |||
interface Props { | |||
branchLike?: T.BranchLike; | |||
@@ -335,8 +336,9 @@ export default class MeasureContent extends React.PureComponent<Props, State> { | |||
{!isFile && | |||
metric && ( | |||
<> | |||
<div>{translate('component_measures.view_as')}</div> | |||
<MeasureViewSelect | |||
className="measure-view-select big-spacer-right" | |||
className="measure-view-select spacer-left big-spacer-right" | |||
handleViewChange={this.updateView} | |||
metric={metric} | |||
view={view} |
@@ -73,10 +73,6 @@ export default class MeasureViewSelect extends React.PureComponent<Props> { | |||
); | |||
}; | |||
renderValue = (value: { icon: JSX.Element }) => { | |||
return value.icon; | |||
}; | |||
render() { | |||
return ( | |||
<Select | |||
@@ -88,7 +84,7 @@ export default class MeasureViewSelect extends React.PureComponent<Props> { | |||
options={this.getOptions()} | |||
searchable={false} | |||
value={this.props.view} | |||
valueRenderer={this.renderValue} | |||
valueRenderer={this.renderOption} | |||
/> | |||
); | |||
} |
@@ -139,7 +139,7 @@ | |||
} | |||
.measure-view-select { | |||
width: 50px; | |||
width: 102px; | |||
} | |||
.measure-view-select .Select-menu-outer { |
@@ -47,6 +47,29 @@ const ISSUE_MEASURES = [ | |||
'new_vulnerabilities' | |||
]; | |||
const issueParamsPerMetric: { [key: string]: { [key: string]: string } } = { | |||
blocker_violations: { resolved: 'false', severities: 'BLOCKER' }, | |||
new_blocker_violations: { resolved: 'false', severities: 'BLOCKER' }, | |||
critical_violations: { resolved: 'false', severities: 'CRITICAL' }, | |||
new_critical_violations: { resolved: 'false', severities: 'CRITICAL' }, | |||
major_violations: { resolved: 'false', severities: 'MAJOR' }, | |||
new_major_violations: { resolved: 'false', severities: 'MAJOR' }, | |||
minor_violations: { resolved: 'false', severities: 'MINOR' }, | |||
new_minor_violations: { resolved: 'false', severities: 'MINOR' }, | |||
info_violations: { resolved: 'false', severities: 'INFO' }, | |||
new_info_violations: { resolved: 'false', severities: 'INFO' }, | |||
open_issues: { resolved: 'false', statuses: 'OPEN' }, | |||
reopened_issues: { resolved: 'false', statuses: 'REOPENED' }, | |||
confirmed_issues: { resolved: 'false', statuses: 'CONFIRMED' }, | |||
false_positive_issues: { resolutions: 'FALSE-POSITIVE' }, | |||
code_smells: { resolved: 'false', types: 'CODE_SMELL' }, | |||
new_code_smells: { resolved: 'false', types: 'CODE_SMELL' }, | |||
bugs: { resolved: 'false', types: 'BUG' }, | |||
new_bugs: { resolved: 'false', types: 'BUG' }, | |||
vulnerabilities: { resolved: 'false', types: 'VULNERABILITY' }, | |||
new_vulnerabilities: { resolved: 'false', types: 'VULNERABILITY' } | |||
}; | |||
interface Props { | |||
branchLike?: T.BranchLike; | |||
children?: React.ReactNode; | |||
@@ -62,60 +85,14 @@ export default class DrilldownLink extends React.PureComponent<Props> { | |||
}; | |||
propsToIssueParams = () => { | |||
const params: { [key: string]: string | boolean } = {}; | |||
const params: { [key: string]: string | boolean } = { | |||
...(issueParamsPerMetric[this.props.metric] || { resolved: 'false' }) | |||
}; | |||
if (this.props.sinceLeakPeriod) { | |||
params.sinceLeakPeriod = true; | |||
} | |||
switch (this.props.metric) { | |||
case 'blocker_violations': | |||
case 'new_blocker_violations': | |||
Object.assign(params, { resolved: 'false', severities: 'BLOCKER' }); | |||
break; | |||
case 'critical_violations': | |||
case 'new_critical_violations': | |||
Object.assign(params, { resolved: 'false', severities: 'CRITICAL' }); | |||
break; | |||
case 'major_violations': | |||
case 'new_major_violations': | |||
Object.assign(params, { resolved: 'false', severities: 'MAJOR' }); | |||
break; | |||
case 'minor_violations': | |||
case 'new_minor_violations': | |||
Object.assign(params, { resolved: 'false', severities: 'MINOR' }); | |||
break; | |||
case 'info_violations': | |||
case 'new_info_violations': | |||
Object.assign(params, { resolved: 'false', severities: 'INFO' }); | |||
break; | |||
case 'open_issues': | |||
Object.assign(params, { resolved: 'false', statuses: 'OPEN' }); | |||
break; | |||
case 'reopened_issues': | |||
Object.assign(params, { resolved: 'false', statuses: 'REOPENED' }); | |||
break; | |||
case 'confirmed_issues': | |||
Object.assign(params, { resolved: 'false', statuses: 'CONFIRMED' }); | |||
break; | |||
case 'false_positive_issues': | |||
Object.assign(params, { resolutions: 'FALSE-POSITIVE' }); | |||
break; | |||
case 'code_smells': | |||
case 'new_code_smells': | |||
Object.assign(params, { resolved: 'false', types: 'CODE_SMELL' }); | |||
break; | |||
case 'bugs': | |||
case 'new_bugs': | |||
Object.assign(params, { resolved: 'false', types: 'BUG' }); | |||
break; | |||
case 'vulnerabilities': | |||
case 'new_vulnerabilities': | |||
Object.assign(params, { resolved: 'false', types: 'VULNERABILITY' }); | |||
break; | |||
default: | |||
Object.assign(params, { resolved: 'false' }); | |||
} | |||
return params; | |||
}; | |||
@@ -140,7 +117,8 @@ export default class DrilldownLink extends React.PureComponent<Props> { | |||
const url = getComponentDrilldownUrl({ | |||
componentKey: this.props.component, | |||
metric: this.props.metric, | |||
branchLike: this.props.branchLike | |||
branchLike: this.props.branchLike, | |||
listView: true | |||
}); | |||
return ( | |||
<Link className={this.props.className} to={url}> |
@@ -0,0 +1,54 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2019 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 * as React from 'react'; | |||
import { shallow } from 'enzyme'; | |||
import DrilldownLink from '../DrilldownLink'; | |||
it('should render correctly', () => { | |||
const wrapper = shallowRender(); | |||
expect(wrapper).toMatchSnapshot(); | |||
}); | |||
it('should render issuesLink correctly', () => { | |||
const wrapper = shallowRender({ metric: 'new_violations' }); | |||
expect(wrapper).toMatchSnapshot(); | |||
}); | |||
describe('propsToIssueParams', () => { | |||
it('should render correct default parameters', () => { | |||
const wrapper = shallowRender(); | |||
expect(wrapper.instance().propsToIssueParams()).toEqual({ resolved: 'false' }); | |||
}); | |||
it(`should render correct params`, () => { | |||
const wrapper = shallowRender({ metric: 'false_positive_issues', sinceLeakPeriod: true }); | |||
expect(wrapper.instance().propsToIssueParams()).toEqual({ | |||
resolutions: 'FALSE-POSITIVE', | |||
sinceLeakPeriod: true | |||
}); | |||
}); | |||
}); | |||
const shallowRender = (props: Partial<DrilldownLink['props']> = {}, label = 'label') => { | |||
return shallow<DrilldownLink>( | |||
<DrilldownLink component="project123" metric="other" {...props}> | |||
{label} | |||
</DrilldownLink> | |||
); | |||
}; |
@@ -0,0 +1,38 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly 1`] = ` | |||
<Link | |||
onlyActiveOnIndex={false} | |||
style={Object {}} | |||
to={ | |||
Object { | |||
"pathname": "/component_measures", | |||
"query": Object { | |||
"id": "project123", | |||
"metric": "other", | |||
"view": "list", | |||
}, | |||
} | |||
} | |||
> | |||
label | |||
</Link> | |||
`; | |||
exports[`should render issuesLink correctly 1`] = ` | |||
<Link | |||
onlyActiveOnIndex={false} | |||
style={Object {}} | |||
to={ | |||
Object { | |||
"pathname": "/project/issues", | |||
"query": Object { | |||
"id": "project123", | |||
"resolved": "false", | |||
}, | |||
} | |||
} | |||
> | |||
label | |||
</Link> | |||
`; |
@@ -114,12 +114,16 @@ export function getComponentDrilldownUrl(options: { | |||
branchLike?: T.BranchLike; | |||
selectionKey?: string; | |||
treemapView?: boolean; | |||
listView?: boolean; | |||
}): Location { | |||
const { componentKey, metric, branchLike, selectionKey, treemapView } = options; | |||
const { componentKey, metric, branchLike, selectionKey, treemapView, listView } = options; | |||
const query: Query = { id: componentKey, metric, ...getBranchLikeQuery(branchLike) }; | |||
if (treemapView) { | |||
query.view = 'treemap'; | |||
} | |||
if (listView) { | |||
query.view = 'list'; | |||
} | |||
if (selectionKey) { | |||
query.selected = selectionKey; | |||
} |
@@ -2576,6 +2576,7 @@ component_measures.show_metric_history=Show history of this metric | |||
component_measures.tab.tree=Tree | |||
component_measures.tab.list=List | |||
component_measures.tab.treemap=Treemap | |||
component_measures.view_as=View as | |||
component_measures.legend.color_x=Color: {0} | |||
component_measures.legend.size_x=Size: {0} | |||
component_measures.legend.worse_of_x_y=Worse of {0} and {1} | |||
@@ -2655,7 +2656,7 @@ organization.avatar=Avatar | |||
organization.avatar.description=Url of a small image that represents the organization (preferably 30px height). | |||
organization.avatar.preview=Preview | |||
organization.bind_to_x=Bind this organization to {0} | |||
organization.go_to_settings_to_bind=Go to Organization Settings to bind it. | |||
organization.go_to_settings_to_bind=Go to Organization Settings to bind it | |||
organization.bound=This organization is bound. | |||
organization.bound_to_x=This organization is bound to {0} | |||
organization.not_bound_to_x=This organization is not bound to {0} |