diff options
author | Grégoire Aubert <gregoire.aubert@sonarsource.com> | 2017-05-26 16:25:49 +0200 |
---|---|---|
committer | Grégoire Aubert <gregoire.aubert@sonarsource.com> | 2017-06-09 08:26:48 +0200 |
commit | eb029461e77fbb1f7a9dde462512d88fc65d46b4 (patch) | |
tree | b67f8fb968ef013b4bdbc3e162e202b899f1471e /server/sonar-web/src/main | |
parent | e4b39a5be78324ec550c7d703c1ea549ffd08e95 (diff) | |
download | sonarqube-eb029461e77fbb1f7a9dde462512d88fc65d46b4.tar.gz sonarqube-eb029461e77fbb1f7a9dde462512d88fc65d46b4.zip |
SONAR-9245 Add all leak facets
Diffstat (limited to 'server/sonar-web/src/main')
23 files changed, 578 insertions, 166 deletions
diff --git a/server/sonar-web/src/main/js/apps/projects/components/PageSidebar.js b/server/sonar-web/src/main/js/apps/projects/components/PageSidebar.js index b6ebb70b8e6..85d9184784d 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/PageSidebar.js +++ b/server/sonar-web/src/main/js/apps/projects/components/PageSidebar.js @@ -22,9 +22,20 @@ import React from 'react'; import { Link } from 'react-router'; import FavoriteFilterContainer from './FavoriteFilterContainer'; import LanguagesFilterContainer from '../filters/LanguagesFilterContainer'; -import PageSidebarOverall from './PageSidebarOverall'; +import CoverageFilter from '../filters/CoverageFilter'; +import DuplicationsFilter from '../filters/DuplicationsFilter'; +import MaintainabilityFilter from '../filters/MaintainabilityFilter'; +import NewCoverageFilter from '../filters/NewCoverageFilter'; +import NewDuplicationsFilter from '../filters/NewDuplicationsFilter'; +import NewMaintainabilityFilter from '../filters/NewMaintainabilityFilter'; +import NewReliabilityFilter from '../filters/NewReliabilityFilter'; +import NewSecurityFilter from '../filters/NewSecurityFilter'; +import NewSizeFilter from '../filters/NewSizeFilter'; import QualityGateFilter from '../filters/QualityGateFilter'; +import ReliabilityFilter from '../filters/ReliabilityFilter'; +import SecurityFilter from '../filters/SecurityFilter'; import SearchFilterContainer from '../filters/SearchFilterContainer'; +import SizeFilter from '../filters/SizeFilter'; import TagsFilterContainer from '../filters/TagsFilterContainer'; import { translate } from '../../../helpers/l10n'; @@ -49,6 +60,7 @@ export default function PageSidebar({ const isLeakView = view === 'leak'; const basePathName = organization ? `/organizations/${organization.key}/projects` : '/projects'; const pathname = basePathName + (isFavorite ? '/favorite' : ''); + const facetProps = { query, isFavorite, organization }; let linkQuery: ?{ view: string, visualization?: string }; if (view !== 'overall') { @@ -72,13 +84,27 @@ export default function PageSidebar({ </div>} <h3>{translate('filters')}</h3> - <SearchFilterContainer query={query} isFavorite={isFavorite} organization={organization} /> + <SearchFilterContainer {...facetProps} /> </div> - <QualityGateFilter query={query} isFavorite={isFavorite} organization={organization} /> - {!isLeakView && - <PageSidebarOverall query={query} isFavorite={isFavorite} organization={organization} />} - <LanguagesFilterContainer query={query} isFavorite={isFavorite} organization={organization} /> - <TagsFilterContainer query={query} isFavorite={isFavorite} organization={organization} /> + <QualityGateFilter {...facetProps} /> + {!isLeakView && [ + <ReliabilityFilter key="reliability" {...facetProps} />, + <SecurityFilter key="security" {...facetProps} />, + <MaintainabilityFilter key="maintainability" {...facetProps} />, + <CoverageFilter key="coverage" {...facetProps} />, + <DuplicationsFilter key="duplications" {...facetProps} />, + <SizeFilter key="size" {...facetProps} /> + ]} + {isLeakView && [ + <NewReliabilityFilter key="new_reliability" {...facetProps} />, + <NewSecurityFilter key="new_security" {...facetProps} />, + <NewMaintainabilityFilter key="new_maintainability" {...facetProps} />, + <NewCoverageFilter key="new_coverage" {...facetProps} />, + <NewDuplicationsFilter key="new_duplications" {...facetProps} />, + <NewSizeFilter key="new_size" {...facetProps} /> + ]} + <LanguagesFilterContainer {...facetProps} /> + <TagsFilterContainer {...facetProps} /> </div> ); } diff --git a/server/sonar-web/src/main/js/apps/projects/components/PageSidebarOverall.js b/server/sonar-web/src/main/js/apps/projects/components/PageSidebarOverall.js deleted file mode 100644 index 67e782ae469..00000000000 --- a/server/sonar-web/src/main/js/apps/projects/components/PageSidebarOverall.js +++ /dev/null @@ -1,46 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2017 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. - */ -//@flow -import React from 'react'; -import CoverageFilter from '../filters/CoverageFilter'; -import DuplicationsFilter from '../filters/DuplicationsFilter'; -import SizeFilter from '../filters/SizeFilter'; -import ReliabilityFilter from '../filters/ReliabilityFilter'; -import SecurityFilter from '../filters/SecurityFilter'; -import MaintainabilityFilter from '../filters/MaintainabilityFilter'; - -type Props = { - isFavorite: boolean, - organization?: { key: string }, - query: { [string]: string } -}; - -export default function PageSidebarOverall({ query, isFavorite, organization }: Props) { - return ( - <div> - <ReliabilityFilter query={query} isFavorite={isFavorite} organization={organization} /> - <SecurityFilter query={query} isFavorite={isFavorite} organization={organization} /> - <MaintainabilityFilter query={query} isFavorite={isFavorite} organization={organization} /> - <CoverageFilter query={query} isFavorite={isFavorite} organization={organization} /> - <DuplicationsFilter query={query} isFavorite={isFavorite} organization={organization} /> - <SizeFilter query={query} isFavorite={isFavorite} organization={organization} /> - </div> - ); -} diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardLeakMeasures.js b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardLeakMeasures.js index e55404cfe42..2b9698e7c19 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardLeakMeasures.js +++ b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardLeakMeasures.js @@ -47,7 +47,7 @@ export default function ProjectCardLeakMeasures({ measures }: Props) { /> <Rating value={measures['new_reliability_rating']} /> </div> - <div className="project-card-measure-label"> + <div className="project-card-measure-label-with-icon"> <BugIcon className="little-spacer-right vertical-bottom" /> {translate('metric.new_bugs.name')} </div> @@ -64,7 +64,7 @@ export default function ProjectCardLeakMeasures({ measures }: Props) { /> <Rating value={measures['new_security_rating']} /> </div> - <div className="project-card-measure-label"> + <div className="project-card-measure-label-with-icon"> <VulnerabilityIcon className="little-spacer-right vertical-bottom" /> {translate('metric.new_vulnerabilities.name')} </div> @@ -81,7 +81,7 @@ export default function ProjectCardLeakMeasures({ measures }: Props) { /> <Rating value={measures['new_maintainability_rating']} /> </div> - <div className="project-card-measure-label"> + <div className="project-card-measure-label-with-icon"> <CodeSmellIcon className="little-spacer-right vertical-bottom" /> {translate('metric.new_code_smells.name')} </div> diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverallMeasures.js b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverallMeasures.js index 219a6650903..7a1c8a31c6c 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverallMeasures.js +++ b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverallMeasures.js @@ -109,7 +109,7 @@ export default function ProjectCardOverallMeasures({ measures }: Props) { {measures['ncloc'] != null && <div className="project-card-measure pull-right" data-key="ncloc"> - <div className="project-card-measure-inner"> + <div className="project-card-measure-inner pull-right"> <div className="project-card-measure-number"> <span className="spacer-right"> <SizeRating value={Number(measures['ncloc'])} /> diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageSidebar-test.js.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageSidebar-test.js.snap index 3bfd6952c23..fdbeb7ed11b 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageSidebar-test.js.snap +++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageSidebar-test.js.snap @@ -57,6 +57,55 @@ exports[`should render \`leak\` view correctly 1`] = ` } } /> + <NewReliabilityFilter + isFavorite={false} + query={ + Object { + "view": "leak", + } + } + /> + <NewSecurityFilter + isFavorite={false} + query={ + Object { + "view": "leak", + } + } + /> + <NewMaintainabilityFilter + isFavorite={false} + query={ + Object { + "view": "leak", + } + } + /> + <NewCoverageFilter + isFavorite={false} + query={ + Object { + "view": "leak", + } + } + /> + <NewDuplicationsFilter + isFavorite={false} + query={ + Object { + "view": "leak", + } + } + /> + <NewSizeFilter + isFavorite={false} + property="new_lines" + query={ + Object { + "view": "leak", + } + } + /> <Connect(withRouter(LanguagesFilter)) isFavorite={false} query={ @@ -119,8 +168,51 @@ exports[`should render correctly 1`] = ` } } /> - <PageSidebarOverall + <ReliabilityFilter + isFavorite={true} + query={ + Object { + "size": "3", + } + } + /> + <SecurityFilter + isFavorite={true} + query={ + Object { + "size": "3", + } + } + /> + <MaintainabilityFilter + isFavorite={true} + query={ + Object { + "size": "3", + } + } + /> + <CoverageFilter + isFavorite={true} + property="coverage" + query={ + Object { + "size": "3", + } + } + /> + <DuplicationsFilter + isFavorite={true} + property="duplications" + query={ + Object { + "size": "3", + } + } + /> + <SizeFilter isFavorite={true} + property="size" query={ Object { "size": "3", diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLeakMeasures-test.js.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLeakMeasures-test.js.snap index 9171745b0da..964ff49ff68 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLeakMeasures-test.js.snap +++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLeakMeasures-test.js.snap @@ -105,7 +105,7 @@ exports[`should render correctly with all data 1`] = ` /> </div> <div - className="project-card-measure-label" + className="project-card-measure-label-with-icon" > <BugIcon className="little-spacer-right vertical-bottom" @@ -145,7 +145,7 @@ exports[`should render correctly with all data 1`] = ` /> </div> <div - className="project-card-measure-label" + className="project-card-measure-label-with-icon" > <VulnerabilityIcon className="little-spacer-right vertical-bottom" @@ -185,7 +185,7 @@ exports[`should render correctly with all data 1`] = ` /> </div> <div - className="project-card-measure-label" + className="project-card-measure-label-with-icon" > <CodeSmellIcon className="little-spacer-right vertical-bottom" diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardOverallMeasures-test.js.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardOverallMeasures-test.js.snap index b5431a1454a..797b56431ad 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardOverallMeasures-test.js.snap +++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardOverallMeasures-test.js.snap @@ -226,7 +226,7 @@ exports[`should render correctly with all data 1`] = ` data-key="ncloc" > <div - className="project-card-measure-inner" + className="project-card-measure-inner pull-right" > <div className="project-card-measure-number" @@ -270,7 +270,7 @@ exports[`should render ncloc correctly 1`] = ` data-key="ncloc" > <div - className="project-card-measure-inner" + className="project-card-measure-inner pull-right" > <div className="project-card-measure-number" diff --git a/server/sonar-web/src/main/js/apps/projects/filters/CoverageFilter.js b/server/sonar-web/src/main/js/apps/projects/filters/CoverageFilter.js index c29c7f26ec6..827e5e482f8 100644 --- a/server/sonar-web/src/main/js/apps/projects/filters/CoverageFilter.js +++ b/server/sonar-web/src/main/js/apps/projects/filters/CoverageFilter.js @@ -27,12 +27,16 @@ import { translate } from '../../../helpers/l10n'; export default class CoverageFilter extends React.PureComponent { static propTypes = { + className: React.PropTypes.string, query: React.PropTypes.object.isRequired, isFavorite: React.PropTypes.bool, - organization: React.PropTypes.object + organization: React.PropTypes.object, + property: React.PropTypes.string }; - property = 'coverage'; + static defaultProps = { + property: 'coverage' + }; getFacetValueForOption(facet, option) { const map = ['80.0-*', '70.0-80.0', '50.0-70.0', '30.0-50.0', '*-30.0']; @@ -57,7 +61,8 @@ export default class CoverageFilter extends React.PureComponent { render() { return ( <FilterContainer - property={this.property} + property={this.props.property} + className={this.props.className} options={[1, 2, 3, 4, 5]} query={this.props.query} renderOption={this.renderOption} @@ -68,7 +73,7 @@ export default class CoverageFilter extends React.PureComponent { header={ <FilterHeader name={translate('metric_domain.Coverage')}> <SortingFilter - property={this.property} + property={this.props.property} query={this.props.query} isFavorite={this.props.isFavorite} organization={this.props.organization} diff --git a/server/sonar-web/src/main/js/apps/projects/filters/DuplicationsFilter.js b/server/sonar-web/src/main/js/apps/projects/filters/DuplicationsFilter.js index e6f2304bc2f..dcda2e9bccf 100644 --- a/server/sonar-web/src/main/js/apps/projects/filters/DuplicationsFilter.js +++ b/server/sonar-web/src/main/js/apps/projects/filters/DuplicationsFilter.js @@ -30,12 +30,16 @@ import { translate } from '../../../helpers/l10n'; export default class DuplicationsFilter extends React.PureComponent { static propTypes = { + className: React.PropTypes.string, query: React.PropTypes.object.isRequired, isFavorite: React.PropTypes.bool, - organization: React.PropTypes.object + organization: React.PropTypes.object, + property: React.PropTypes.string }; - property = 'duplications'; + static defaultProps = { + property: 'duplications' + }; getFacetValueForOption(facet, option) { const map = ['*-3.0', '3.0-5.0', '5.0-10.0', '10.0-20.0', '20.0-*']; @@ -60,7 +64,8 @@ export default class DuplicationsFilter extends React.PureComponent { render() { return ( <FilterContainer - property={this.property} + property={this.props.property} + className={this.props.className} options={[1, 2, 3, 4, 5]} query={this.props.query} renderOption={this.renderOption} @@ -71,7 +76,7 @@ export default class DuplicationsFilter extends React.PureComponent { header={ <FilterHeader name={translate('metric_domain.Duplications')}> <SortingFilter - property={this.property} + property={this.props.property} query={this.props.query} isFavorite={this.props.isFavorite} organization={this.props.organization} diff --git a/server/sonar-web/src/main/js/apps/projects/filters/Filter.js b/server/sonar-web/src/main/js/apps/projects/filters/Filter.js index 1f8677e3ee7..68073fa0823 100644 --- a/server/sonar-web/src/main/js/apps/projects/filters/Filter.js +++ b/server/sonar-web/src/main/js/apps/projects/filters/Filter.js @@ -27,6 +27,7 @@ import { translate } from '../../../helpers/l10n'; export default class Filter extends React.PureComponent { static propTypes = { property: React.PropTypes.string.isRequired, + className: React.PropTypes.string, options: React.PropTypes.array.isRequired, query: React.PropTypes.object.isRequired, renderOption: React.PropTypes.func.isRequired, @@ -146,7 +147,9 @@ export default class Filter extends React.PureComponent { render() { return ( - <div className="search-navigator-facet-box" data-key={this.props.property}> + <div + className={classNames('search-navigator-facet-box', this.props.className)} + data-key={this.props.property}> {this.props.header} {this.renderOptions()} {this.props.footer} diff --git a/server/sonar-web/src/main/js/apps/projects/filters/IssuesFilter.js b/server/sonar-web/src/main/js/apps/projects/filters/IssuesFilter.js index a287639d59c..52e52bc92a2 100644 --- a/server/sonar-web/src/main/js/apps/projects/filters/IssuesFilter.js +++ b/server/sonar-web/src/main/js/apps/projects/filters/IssuesFilter.js @@ -20,17 +20,18 @@ import React from 'react'; import FilterContainer from './FilterContainer'; import FilterHeader from './FilterHeader'; -import SortingFilter from './SortingFilter'; import Rating from '../../../components/ui/Rating'; import { translate } from '../../../helpers/l10n'; export default class IssuesFilter extends React.PureComponent { static propTypes = { - property: React.PropTypes.string.isRequired, - name: React.PropTypes.string.isRequired, - query: React.PropTypes.object.isRequired, + className: React.PropTypes.string, + headerDetail: React.PropTypes.element, isFavorite: React.PropTypes.bool, - organization: React.PropTypes.object + organization: React.PropTypes.object, + name: React.PropTypes.string.isRequired, + property: React.PropTypes.string.isRequired, + query: React.PropTypes.object.isRequired }; getFacetValueForOption(facet, option) { @@ -52,6 +53,7 @@ export default class IssuesFilter extends React.PureComponent { return ( <FilterContainer property={this.props.property} + className={this.props.className} options={[1, 2, 3, 4, 5]} query={this.props.query} renderOption={this.renderOption} @@ -61,12 +63,7 @@ export default class IssuesFilter extends React.PureComponent { highlightUnder={1} header={ <FilterHeader name={translate('metric_domain', this.props.name)}> - <SortingFilter - property={this.props.property} - query={this.props.query} - isFavorite={this.props.isFavorite} - organization={this.props.organization} - /> + {this.props.headerDetail} </FilterHeader> } /> diff --git a/server/sonar-web/src/main/js/apps/projects/filters/NewCoverageFilter.js b/server/sonar-web/src/main/js/apps/projects/filters/NewCoverageFilter.js new file mode 100644 index 00000000000..24f24ce2706 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projects/filters/NewCoverageFilter.js @@ -0,0 +1,25 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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 React from 'react'; +import CoverageFilter from './CoverageFilter'; + +export default function NewCoverageFilter(props) { + return <CoverageFilter {...props} property="new_coverage" className="leak-facet-box" />; +} diff --git a/server/sonar-web/src/main/js/apps/projects/filters/NewDuplicationsFilter.js b/server/sonar-web/src/main/js/apps/projects/filters/NewDuplicationsFilter.js new file mode 100644 index 00000000000..98754c93080 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projects/filters/NewDuplicationsFilter.js @@ -0,0 +1,25 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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 React from 'react'; +import DuplicationsFilter from './DuplicationsFilter'; + +export default function NewDuplicationsFilter(props) { + return <DuplicationsFilter {...props} property="new_duplications" className="leak-facet-box" />; +} diff --git a/server/sonar-web/src/main/js/apps/projects/filters/NewMaintainabilityFilter.js b/server/sonar-web/src/main/js/apps/projects/filters/NewMaintainabilityFilter.js new file mode 100644 index 00000000000..b501476a25f --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projects/filters/NewMaintainabilityFilter.js @@ -0,0 +1,42 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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 React from 'react'; +import CodeSmellIcon from '../../../components/icons-components/CodeSmellIcon'; +import IssuesFilter from './IssuesFilter'; +import { translate } from '../../../helpers/l10n'; + +export default function NewMaintainabilityFilter(props) { + return ( + <IssuesFilter + {...props} + className="leak-facet-box" + headerDetail={ + <span className="note little-spacer-left"> + {'('} + <CodeSmellIcon className="little-spacer-right" /> + {translate('metric.code_smells.name')} + {' )'} + </span> + } + name="Maintainability" + property="new_maintainability" + /> + ); +} diff --git a/server/sonar-web/src/main/js/apps/projects/filters/NewReliabilityFilter.js b/server/sonar-web/src/main/js/apps/projects/filters/NewReliabilityFilter.js new file mode 100644 index 00000000000..0dbb948a577 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projects/filters/NewReliabilityFilter.js @@ -0,0 +1,39 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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 React from 'react'; +import BugIcon from '../../../components/icons-components/BugIcon'; +import IssuesFilter from './IssuesFilter'; +import { translate } from '../../../helpers/l10n'; + +export default function NewReliabilityFilter(props) { + return ( + <IssuesFilter + {...props} + className="leak-facet-box" + headerDetail={ + <span className="note little-spacer-left"> + {'('}<BugIcon className="little-spacer-right" />{translate('metric.bugs.name')}{' )'} + </span> + } + name="Reliability" + property="new_reliability" + /> + ); +} diff --git a/server/sonar-web/src/main/js/apps/projects/filters/NewSecurityFilter.js b/server/sonar-web/src/main/js/apps/projects/filters/NewSecurityFilter.js new file mode 100644 index 00000000000..7d2e64e74c7 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projects/filters/NewSecurityFilter.js @@ -0,0 +1,42 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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 React from 'react'; +import VulnerabilityIcon from '../../../components/icons-components/VulnerabilityIcon'; +import IssuesFilter from './IssuesFilter'; +import { translate } from '../../../helpers/l10n'; + +export default function NewSecurityFilter(props) { + return ( + <IssuesFilter + {...props} + className="leak-facet-box" + headerDetail={ + <span className="note little-spacer-left"> + {'('} + <VulnerabilityIcon className="little-spacer-right" /> + {translate('metric.vulnerabilities.name')} + {' )'} + </span> + } + name="Security" + property="new_security" + /> + ); +} diff --git a/server/sonar-web/src/main/js/apps/projects/filters/NewSizeFilter.js b/server/sonar-web/src/main/js/apps/projects/filters/NewSizeFilter.js new file mode 100644 index 00000000000..c672d6c0ce4 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projects/filters/NewSizeFilter.js @@ -0,0 +1,86 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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 React from 'react'; +import FilterContainer from './FilterContainer'; +import FilterHeader from './FilterHeader'; +import SortingFilter from './SortingFilter'; +import { translate } from '../../../helpers/l10n'; +import { getSizeRatingLabel } from '../../../helpers/ratings'; + +export default class NewSizeFilter extends React.PureComponent { + static propTypes = { + className: React.PropTypes.string, + query: React.PropTypes.object.isRequired, + isFavorite: React.PropTypes.bool, + organization: React.PropTypes.object, + property: React.PropTypes.string + }; + + static defaultProps = { + property: 'new_lines' + }; + + getFacetValueForOption(facet, option) { + const map = [ + '*-1000.0', + '1000.0-10000.0', + '10000.0-100000.0', + '100000.0-500000.0', + '500000.0-*' + ]; + return facet[map[option - 1]]; + } + + renderOption(option) { + return ( + <span> + {getSizeRatingLabel(option)} + </span> + ); + } + + render() { + return ( + <FilterContainer + property={this.props.property} + className="leak-facet-box" + options={[1, 2, 3, 4, 5]} + query={this.props.query} + renderOption={this.renderOption} + isFavorite={this.props.isFavorite} + organization={this.props.organization} + getFacetValueForOption={this.getFacetValueForOption} + highlightUnder={1} + header={ + <FilterHeader name={translate('metric_domain.new_size')}> + <SortingFilter + property={this.props.property} + query={this.props.query} + isFavorite={this.props.isFavorite} + organization={this.props.organization} + leftText={translate('biggest')} + rightText={translate('smallest')} + /> + </FilterHeader> + } + /> + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/projects/filters/SizeFilter.js b/server/sonar-web/src/main/js/apps/projects/filters/SizeFilter.js index c9cb96b15bc..aedd2d966cd 100644 --- a/server/sonar-web/src/main/js/apps/projects/filters/SizeFilter.js +++ b/server/sonar-web/src/main/js/apps/projects/filters/SizeFilter.js @@ -27,12 +27,16 @@ import { getSizeRatingLabel, getSizeRatingAverageValue } from '../../../helpers/ export default class SizeFilter extends React.PureComponent { static propTypes = { + className: React.PropTypes.string, query: React.PropTypes.object.isRequired, isFavorite: React.PropTypes.bool, - organization: React.PropTypes.object + organization: React.PropTypes.object, + property: React.PropTypes.string }; - property = 'size'; + static defaultProps = { + property: 'size' + }; getFacetValueForOption(facet, option) { const map = [ @@ -56,23 +60,11 @@ export default class SizeFilter extends React.PureComponent { ); } - renderSort = () => { - return ( - <SortingFilter - property={this.property} - query={this.props.query} - isFavorite={this.props.isFavorite} - organization={this.props.organization} - leftText={translate('biggest')} - rightText={translate('smallest')} - /> - ); - }; - render() { return ( <FilterContainer - property={this.property} + property={this.props.property} + className={this.props.className} options={[1, 2, 3, 4, 5]} query={this.props.query} renderOption={this.renderOption} @@ -83,7 +75,7 @@ export default class SizeFilter extends React.PureComponent { header={ <FilterHeader name={translate('metric_domain.Size')}> <SortingFilter - property={this.property} + property={this.props.property} query={this.props.query} isFavorite={this.props.isFavorite} organization={this.props.organization} diff --git a/server/sonar-web/src/main/js/apps/projects/store/actions.js b/server/sonar-web/src/main/js/apps/projects/store/actions.js index 38a2d21caca..46dc1f670a1 100644 --- a/server/sonar-web/src/main/js/apps/projects/store/actions.js +++ b/server/sonar-web/src/main/js/apps/projects/store/actions.js @@ -82,6 +82,18 @@ const FACETS = [ 'tags' ]; +const LEAK_FACETS = [ + 'new_reliability_rating', + 'new_security_rating', + 'new_maintainability_rating', + 'new_coverage', + 'new_duplicated_lines_density', + 'new_lines', + 'alert_status', + 'languages', + 'tags' +]; + const onFail = dispatch => error => { parseError(error).then(message => dispatch(addGlobalErrorMessage(message))); dispatch(updateState({ loading: false })); @@ -124,6 +136,13 @@ const defineMetrics = query => { } }; +const defineFacets = query => { + if (query.view === 'leak') { + return LEAK_FACETS; + } + return FACETS; +}; + const fetchProjectMeasures = (projects, query) => dispatch => { if (!projects.length) { return Promise.resolve(); @@ -193,7 +212,7 @@ export const fetchProjects = (query, isFavorite, organization) => dispatch => { const ps = query.view === 'visualizations' ? PAGE_SIZE_VISUALIZATIONS : PAGE_SIZE; const data = convertToQueryData(query, isFavorite, organization, { ps, - facets: FACETS.join(), + facets: defineFacets(query).join(), f: 'analysisDate,leakPeriodDate' }); return searchProjects(data).then(onReceiveProjects(dispatch, query), onFail(dispatch)); diff --git a/server/sonar-web/src/main/js/apps/projects/store/facetsDuck.js b/server/sonar-web/src/main/js/apps/projects/store/facetsDuck.js index 953fcaf4101..f47b8d67471 100644 --- a/server/sonar-web/src/main/js/apps/projects/store/facetsDuck.js +++ b/server/sonar-web/src/main/js/apps/projects/store/facetsDuck.js @@ -24,14 +24,20 @@ import { mapMetricToProperty } from './utils'; const CUMULATIVE_FACETS = [ 'reliability', + 'new_reliability', 'security', + 'new_security', 'maintainability', + 'new_maintainability', 'coverage', + 'new_coverage', 'duplications', - 'size' + 'new_duplications', + 'size', + 'new_lines' ]; -const REVERSED_FACETS = ['coverage']; +const REVERSED_FACETS = ['coverage', 'new_coverage']; const mapFacetValues = values => { const map = {}; diff --git a/server/sonar-web/src/main/js/apps/projects/store/utils.js b/server/sonar-web/src/main/js/apps/projects/store/utils.js index 0316f71ab7f..13db6e4c7cb 100644 --- a/server/sonar-web/src/main/js/apps/projects/store/utils.js +++ b/server/sonar-web/src/main/js/apps/projects/store/utils.js @@ -57,11 +57,17 @@ const getVisualization = value => { export const parseUrlQuery = urlQuery => ({ gate: getAsLevel(urlQuery['gate']), reliability: getAsNumericRating(urlQuery['reliability']), + new_reliability: getAsNumericRating(urlQuery['new_reliability']), security: getAsNumericRating(urlQuery['security']), + new_security: getAsNumericRating(urlQuery['new_security']), maintainability: getAsNumericRating(urlQuery['maintainability']), + new_maintainability: getAsNumericRating(urlQuery['new_maintainability']), coverage: getAsNumericRating(urlQuery['coverage']), + new_coverage: getAsNumericRating(urlQuery['new_coverage']), duplications: getAsNumericRating(urlQuery['duplications']), + new_duplications: getAsNumericRating(urlQuery['new_duplications']), size: getAsNumericRating(urlQuery['size']), + new_lines: getAsNumericRating(urlQuery['new_lines']), languages: getAsArray(urlQuery['languages'], getAsString), tags: getAsArray(urlQuery['tags'], getAsString), search: getAsString(urlQuery['search']), @@ -73,11 +79,17 @@ export const parseUrlQuery = urlQuery => ({ export const mapMetricToProperty = metricKey => { const map = { reliability_rating: 'reliability', + new_reliability_rating: 'new_reliability', security_rating: 'security', + new_security_rating: 'new_security', sqale_rating: 'maintainability', + new_maintainability_rating: 'new_maintainability', coverage: 'coverage', + new_coverage: 'new_coverage', duplicated_lines_density: 'duplications', + new_duplicated_lines_density: 'new_duplications', ncloc: 'size', + new_lines: 'new_lines', alert_status: 'gate', languages: 'languages', tags: 'tags', @@ -89,11 +101,17 @@ export const mapMetricToProperty = metricKey => { export const mapPropertyToMetric = property => { const map = { reliability: 'reliability_rating', + new_reliability: 'new_reliability_rating', security: 'security_rating', + new_security: 'new_security_rating', maintainability: 'sqale_rating', + new_maintainability: 'new_maintainability_rating', coverage: 'coverage', + new_coverage: 'new_coverage', duplications: 'duplicated_lines_density', + new_duplications: 'new_duplicated_lines_density', size: 'ncloc', + new_lines: 'new_lines', gate: 'alert_status', languages: 'languages', tags: 'tags', @@ -110,57 +128,70 @@ const convertIssuesRating = (metric, rating) => { } }; -const convertCoverage = coverage => { +const convertCoverage = (metric, coverage) => { switch (coverage) { case 1: - return mapPropertyToMetric('coverage') + ' >= 80'; + return metric + ' >= 80'; case 2: - return mapPropertyToMetric('coverage') + ' < 80'; + return metric + ' < 80'; case 3: - return mapPropertyToMetric('coverage') + ' < 70'; + return metric + ' < 70'; case 4: - return mapPropertyToMetric('coverage') + ' < 50'; + return metric + ' < 50'; case 5: - return mapPropertyToMetric('coverage') + ' < 30'; + return metric + ' < 30'; default: return ''; } }; -const convertDuplications = duplications => { +const convertDuplications = (metric, duplications) => { switch (duplications) { case 1: - return mapPropertyToMetric('duplications') + ' < 3'; + return metric + ' < 3'; case 2: - return mapPropertyToMetric('duplications') + ' >= 3'; + return metric + ' >= 3'; case 3: - return mapPropertyToMetric('duplications') + ' >= 5'; + return metric + ' >= 5'; case 4: - return mapPropertyToMetric('duplications') + ' >= 10'; + return metric + ' >= 10'; case 5: - return mapPropertyToMetric('duplications') + ' >= 20'; + return metric + ' >= 20'; default: return ''; } }; -const convertSize = size => { +const convertSize = (metric, size) => { switch (size) { case 1: - return mapPropertyToMetric('size') + ' < 1000'; + return metric + ' < 1000'; case 2: - return mapPropertyToMetric('size') + ' >= 1000'; + return metric + ' >= 1000'; case 3: - return mapPropertyToMetric('size') + ' >= 10000'; + return metric + ' >= 10000'; case 4: - return mapPropertyToMetric('size') + ' >= 100000'; + return metric + ' >= 100000'; case 5: - return mapPropertyToMetric('size') + ' >= 500000'; + return metric + ' >= 500000'; default: return ''; } }; +const convertArrayMetric = (metric, items) => { + if (!Array.isArray(items) || items.length < 2) { + return metric + ' = ' + items; + } + return `${metric} IN (${items.join(', ')})`; +}; + +const pushMetricToArray = (query, property, conditionsArray, convertFunction) => { + if (query[property] != null) { + conditionsArray.push(convertFunction(mapPropertyToMetric(property), query[property])); + } +}; + export const convertToFilter = (query, isFavorite) => { const conditions = []; @@ -172,34 +203,30 @@ export const convertToFilter = (query, isFavorite) => { conditions.push(mapPropertyToMetric('gate') + ' = ' + query['gate']); } - if (query['coverage'] != null) { - conditions.push(convertCoverage(query['coverage'])); - } + ['coverage', 'new_coverage'].forEach(property => + pushMetricToArray(query, property, conditions, convertCoverage) + ); - if (query['duplications'] != null) { - conditions.push(convertDuplications(query['duplications'])); - } + ['duplications', 'new_duplications'].forEach(property => + pushMetricToArray(query, property, conditions, convertDuplications) + ); - if (query['size'] != null) { - conditions.push(convertSize(query['size'])); - } + ['size', 'new_lines'].forEach(property => + pushMetricToArray(query, property, conditions, convertSize) + ); - ['reliability', 'security', 'maintainability'].forEach(property => { - if (query[property] != null) { - conditions.push(convertIssuesRating(mapPropertyToMetric(property), query[property])); - } - }); + [ + 'reliability', + 'security', + 'maintainability', + 'new_reliability', + 'new_security', + 'new_maintainability' + ].forEach(property => pushMetricToArray(query, property, conditions, convertIssuesRating)); - ['languages', 'tags'].forEach(property => { - const items = query[property]; - if (items != null) { - if (!Array.isArray(items) || items.length < 2) { - conditions.push(mapPropertyToMetric(property) + ' = ' + items); - } else { - conditions.push(`${mapPropertyToMetric(property)} IN (${items.join(', ')})`); - } - } - }); + ['languages', 'tags'].forEach(property => + pushMetricToArray(query, property, conditions, convertArrayMetric) + ); if (query['search'] != null) { conditions.push(`${mapPropertyToMetric('search')} = "${query['search']}"`); diff --git a/server/sonar-web/src/main/less/components/search-navigator.less b/server/sonar-web/src/main/less/components/search-navigator.less index e01e2dc742a..23c8a7b3ff4 100644 --- a/server/sonar-web/src/main/less/components/search-navigator.less +++ b/server/sonar-web/src/main/less/components/search-navigator.less @@ -74,12 +74,21 @@ .search-navigator-facet-box { background-color: @barBackgroundColor; font-size: @baseFontSize; + + &.leak-facet-box { + background-color: @leakBackgroundColor; + border: 1px solid @leakBorderColor; + } } -.search-navigator-facet-box:not(.hidden) + .search-navigator-facet-box { +.search-navigator-facet-box:not(.hidden):not(.leak-facet-box) + .search-navigator-facet-box:not(.leak-facet-box) { border-top: 1px solid @barBorderColor; } +.leak-facet-box:not(.hidden) + .leak-facet-box { + border-top: none; +} + .search-navigator-facet-box-collapsed { background-color: transparent; @@ -198,32 +207,46 @@ background-color: @red; color: #fff; } +} - &.active { - border: 1px solid @blue; - padding: 3px 5px; - background-color: @lightBlue; - text-decoration: none; +.leak-facet-box .search-navigator-facet { + .facet-name { + background-color: @leakBackgroundColor; + } - .facet-name { - background-color: @lightBlue; + .facet-stat { + background-color: @leakBackgroundColor; + + &:before { + background-image: linear-gradient(to right, fade(@leakBackgroundColor, 0%), @leakBackgroundColor 75%); } + } +} - .facet-stat { - border-color: @blue; - background-color: @lightBlue; - top: -1px; - right: -1px; +.search-navigator-facet.active { + border: 1px solid @blue; + padding: 3px 5px; + background-color: @lightBlue; + text-decoration: none; - &:before { - background-image: linear-gradient(to right, fade(@lightBlue, 0%), @lightBlue 75%); - } - } + .facet-name { + background-color: @lightBlue; + } - .facet-toggle { - display: inline; + .facet-stat { + border-color: @blue; + background-color: @lightBlue; + top: -1px; + right: -1px; + + &:before { + background-image: linear-gradient(to right, fade(@lightBlue, 0%), @lightBlue 75%); } } + + .facet-toggle { + display: inline; + } } .search-navigator-facet-indent { @@ -309,6 +332,10 @@ border-bottom: none; color: @baseFontColor; font-weight: 600; + + & > .note { + font-weight: 400; + } } .search-navigator-facet-header-button { diff --git a/server/sonar-web/src/main/less/variables.less b/server/sonar-web/src/main/less/variables.less index 7f0a91caf8e..ca3a1a87f0d 100644 --- a/server/sonar-web/src/main/less/variables.less +++ b/server/sonar-web/src/main/less/variables.less @@ -89,7 +89,8 @@ @issueBackgroundColor: #ffeaea; @issueBorderColor: desaturate(darken(@issueBackgroundColor, 40%), 30%); - +@leakBackgroundColor: #fbf3d5; +@leakBorderColor: #eae3c7; /* * Sizes @@ -168,7 +169,6 @@ - /* * Page */ |