diff options
8 files changed, 132 insertions, 59 deletions
diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx index 7b3f6325b36..55d400e4ec3 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureContent.tsx @@ -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} diff --git a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureViewSelect.tsx b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureViewSelect.tsx index 236c1217645..31ec863137f 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/components/MeasureViewSelect.tsx +++ b/server/sonar-web/src/main/js/apps/component-measures/components/MeasureViewSelect.tsx @@ -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} /> ); } diff --git a/server/sonar-web/src/main/js/apps/component-measures/style.css b/server/sonar-web/src/main/js/apps/component-measures/style.css index c09c06c3d1a..a7eaa81ac1a 100644 --- a/server/sonar-web/src/main/js/apps/component-measures/style.css +++ b/server/sonar-web/src/main/js/apps/component-measures/style.css @@ -139,7 +139,7 @@ } .measure-view-select { - width: 50px; + width: 102px; } .measure-view-select .Select-menu-outer { diff --git a/server/sonar-web/src/main/js/components/shared/DrilldownLink.tsx b/server/sonar-web/src/main/js/components/shared/DrilldownLink.tsx index 019a32e601c..0d69d1afa7c 100644 --- a/server/sonar-web/src/main/js/components/shared/DrilldownLink.tsx +++ b/server/sonar-web/src/main/js/components/shared/DrilldownLink.tsx @@ -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}> diff --git a/server/sonar-web/src/main/js/components/shared/__tests__/DrilldownLink-test.tsx b/server/sonar-web/src/main/js/components/shared/__tests__/DrilldownLink-test.tsx new file mode 100644 index 00000000000..7cea95baf70 --- /dev/null +++ b/server/sonar-web/src/main/js/components/shared/__tests__/DrilldownLink-test.tsx @@ -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> + ); +}; diff --git a/server/sonar-web/src/main/js/components/shared/__tests__/__snapshots__/DrilldownLink-test.tsx.snap b/server/sonar-web/src/main/js/components/shared/__tests__/__snapshots__/DrilldownLink-test.tsx.snap new file mode 100644 index 00000000000..43494b4d2bf --- /dev/null +++ b/server/sonar-web/src/main/js/components/shared/__tests__/__snapshots__/DrilldownLink-test.tsx.snap @@ -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> +`; diff --git a/server/sonar-web/src/main/js/helpers/urls.ts b/server/sonar-web/src/main/js/helpers/urls.ts index 72db686a0c9..8b9f7a35a0c 100644 --- a/server/sonar-web/src/main/js/helpers/urls.ts +++ b/server/sonar-web/src/main/js/helpers/urls.ts @@ -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; } 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 6759d6c8aff..e094ae31687 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -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} |