Browse Source

SONAR-9245 Add all leak facets

tags/6.5-M1
Grégoire Aubert 7 years ago
parent
commit
eb029461e7
24 changed files with 579 additions and 166 deletions
  1. 33
    7
      server/sonar-web/src/main/js/apps/projects/components/PageSidebar.js
  2. 0
    46
      server/sonar-web/src/main/js/apps/projects/components/PageSidebarOverall.js
  3. 3
    3
      server/sonar-web/src/main/js/apps/projects/components/ProjectCardLeakMeasures.js
  4. 1
    1
      server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverallMeasures.js
  5. 93
    1
      server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageSidebar-test.js.snap
  6. 3
    3
      server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLeakMeasures-test.js.snap
  7. 2
    2
      server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardOverallMeasures-test.js.snap
  8. 9
    4
      server/sonar-web/src/main/js/apps/projects/filters/CoverageFilter.js
  9. 9
    4
      server/sonar-web/src/main/js/apps/projects/filters/DuplicationsFilter.js
  10. 4
    1
      server/sonar-web/src/main/js/apps/projects/filters/Filter.js
  11. 8
    11
      server/sonar-web/src/main/js/apps/projects/filters/IssuesFilter.js
  12. 25
    0
      server/sonar-web/src/main/js/apps/projects/filters/NewCoverageFilter.js
  13. 25
    0
      server/sonar-web/src/main/js/apps/projects/filters/NewDuplicationsFilter.js
  14. 42
    0
      server/sonar-web/src/main/js/apps/projects/filters/NewMaintainabilityFilter.js
  15. 39
    0
      server/sonar-web/src/main/js/apps/projects/filters/NewReliabilityFilter.js
  16. 42
    0
      server/sonar-web/src/main/js/apps/projects/filters/NewSecurityFilter.js
  17. 86
    0
      server/sonar-web/src/main/js/apps/projects/filters/NewSizeFilter.js
  18. 9
    17
      server/sonar-web/src/main/js/apps/projects/filters/SizeFilter.js
  19. 20
    1
      server/sonar-web/src/main/js/apps/projects/store/actions.js
  20. 8
    2
      server/sonar-web/src/main/js/apps/projects/store/facetsDuck.js
  21. 69
    42
      server/sonar-web/src/main/js/apps/projects/store/utils.js
  22. 46
    19
      server/sonar-web/src/main/less/components/search-navigator.less
  23. 2
    2
      server/sonar-web/src/main/less/variables.less
  24. 1
    0
      sonar-core/src/main/resources/org/sonar/l10n/core.properties

+ 33
- 7
server/sonar-web/src/main/js/apps/projects/components/PageSidebar.js View File

@@ -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>
);
}

+ 0
- 46
server/sonar-web/src/main/js/apps/projects/components/PageSidebarOverall.js View File

@@ -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>
);
}

+ 3
- 3
server/sonar-web/src/main/js/apps/projects/components/ProjectCardLeakMeasures.js View File

@@ -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>

+ 1
- 1
server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverallMeasures.js View File

@@ -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'])} />

+ 93
- 1
server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageSidebar-test.js.snap View File

@@ -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",

+ 3
- 3
server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLeakMeasures-test.js.snap View File

@@ -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"

+ 2
- 2
server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardOverallMeasures-test.js.snap View File

@@ -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"

+ 9
- 4
server/sonar-web/src/main/js/apps/projects/filters/CoverageFilter.js View File

@@ -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}

+ 9
- 4
server/sonar-web/src/main/js/apps/projects/filters/DuplicationsFilter.js View File

@@ -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}

+ 4
- 1
server/sonar-web/src/main/js/apps/projects/filters/Filter.js View File

@@ -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}

+ 8
- 11
server/sonar-web/src/main/js/apps/projects/filters/IssuesFilter.js View File

@@ -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>
}
/>

+ 25
- 0
server/sonar-web/src/main/js/apps/projects/filters/NewCoverageFilter.js View File

@@ -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" />;
}

+ 25
- 0
server/sonar-web/src/main/js/apps/projects/filters/NewDuplicationsFilter.js View File

@@ -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" />;
}

+ 42
- 0
server/sonar-web/src/main/js/apps/projects/filters/NewMaintainabilityFilter.js View File

@@ -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"
/>
);
}

+ 39
- 0
server/sonar-web/src/main/js/apps/projects/filters/NewReliabilityFilter.js View File

@@ -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"
/>
);
}

+ 42
- 0
server/sonar-web/src/main/js/apps/projects/filters/NewSecurityFilter.js View File

@@ -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"
/>
);
}

+ 86
- 0
server/sonar-web/src/main/js/apps/projects/filters/NewSizeFilter.js View File

@@ -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>
}
/>
);
}
}

+ 9
- 17
server/sonar-web/src/main/js/apps/projects/filters/SizeFilter.js View File

@@ -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}

+ 20
- 1
server/sonar-web/src/main/js/apps/projects/store/actions.js View File

@@ -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));

+ 8
- 2
server/sonar-web/src/main/js/apps/projects/store/facetsDuck.js View File

@@ -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 = {};

+ 69
- 42
server/sonar-web/src/main/js/apps/projects/store/utils.js View File

@@ -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']}"`);

+ 46
- 19
server/sonar-web/src/main/less/components/search-navigator.less View File

@@ -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 {

+ 2
- 2
server/sonar-web/src/main/less/variables.less View File

@@ -89,7 +89,8 @@
@issueBackgroundColor: #ffeaea;
@issueBorderColor: desaturate(darken(@issueBackgroundColor, 40%), 30%);


@leakBackgroundColor: #fbf3d5;
@leakBorderColor: #eae3c7;

/*
* Sizes
@@ -168,7 +169,6 @@




/*
* Page
*/

+ 1
- 0
sonar-core/src/main/resources/org/sonar/l10n/core.properties View File

@@ -1939,6 +1939,7 @@ severity.INFO.description=Neither a bug nor a quality flaw. Just a finding.
#------------------------------------------------------------------------------

metric_domain.Size=Size
metric_domain.new_size=New Lines
metric_domain.Tests=Tests
metric_domain.Integration Tests=Integration Tests
metric_domain.Complexity=Complexity

Loading…
Cancel
Save