@@ -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> | |||
); | |||
} |
@@ -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> | |||
); | |||
} |
@@ -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> |
@@ -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'])} /> |
@@ -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", |
@@ -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" |
@@ -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" |
@@ -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} |
@@ -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} |
@@ -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} |
@@ -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> | |||
} | |||
/> |
@@ -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" />; | |||
} |
@@ -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" />; | |||
} |
@@ -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" | |||
/> | |||
); | |||
} |
@@ -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" | |||
/> | |||
); | |||
} |
@@ -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" | |||
/> | |||
); | |||
} |
@@ -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> | |||
} | |||
/> | |||
); | |||
} | |||
} |
@@ -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} |
@@ -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)); |
@@ -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 = {}; |
@@ -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']}"`); |
@@ -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 { |
@@ -89,7 +89,8 @@ | |||
@issueBackgroundColor: #ffeaea; | |||
@issueBorderColor: desaturate(darken(@issueBackgroundColor, 40%), 30%); | |||
@leakBackgroundColor: #fbf3d5; | |||
@leakBorderColor: #eae3c7; | |||
/* | |||
* Sizes | |||
@@ -168,7 +169,6 @@ | |||
/* | |||
* Page | |||
*/ |
@@ -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 |