@@ -21,7 +21,7 @@ | |||
import React from 'react'; | |||
import Helmet from 'react-helmet'; | |||
import PageHeaderContainer from './PageHeaderContainer'; | |||
import ProjectsOptionBar from './ProjectsOptionBar'; | |||
import ProjectsOptionBarContainer from './ProjectsOptionBarContainer'; | |||
import ProjectsListContainer from './ProjectsListContainer'; | |||
import ProjectsListFooterContainer from './ProjectsListFooterContainer'; | |||
import PageSidebar from './PageSidebar'; | |||
@@ -129,7 +129,7 @@ export default class AllProjects extends React.PureComponent { | |||
<div> | |||
<Helmet title={translate('projects.page')} /> | |||
<ProjectsOptionBar | |||
<ProjectsOptionBarContainer | |||
onPerspectiveChange={this.handlePerspectiveChange} | |||
onSortChange={this.handleSortChange} | |||
onToggleOptionBar={this.handleOptionBarToggle} |
@@ -54,7 +54,7 @@ export default function PageSidebar({ | |||
visualization | |||
}: Props) { | |||
const isFiltered = Object.keys(query) | |||
.filter(key => key !== 'view' && key !== 'visualization') | |||
.filter(key => !['view', 'visualization', 'sort'].includes(key)) | |||
.some(key => query[key] != null); | |||
const isLeakView = view === 'leak'; | |||
const basePathName = organization ? `/organizations/${organization.key}/projects` : '/projects'; |
@@ -21,14 +21,18 @@ | |||
import React from 'react'; | |||
import classNames from 'classnames'; | |||
import CloseIcon from '../../../components/icons-components/CloseIcon'; | |||
import Tooltip from '../../../components/controls/Tooltip'; | |||
import PerspectiveSelect from './PerspectiveSelect'; | |||
import ProjectsSortingSelect from './ProjectsSortingSelect'; | |||
import { translate } from '../../../helpers/l10n'; | |||
type Props = { | |||
onPerspectiveChange: ({ view: string, visualization?: string }) => void, | |||
onSortChange: (sort: string, desc: boolean) => void, | |||
onToggleOptionBar: boolean => void, | |||
open: boolean, | |||
projects: Array<*>, | |||
projectsAppState: { loading: boolean, total?: number }, | |||
selectedSort: string, | |||
view: string, | |||
visualization?: string | |||
@@ -43,6 +47,37 @@ export default class ProjectsOptionBar extends React.PureComponent { | |||
this.props.onToggleOptionBar(false); | |||
}; | |||
renderSortingSelect() { | |||
const { projectsAppState, projects, view } = this.props; | |||
const limitReached = | |||
projects != null && | |||
projectsAppState.total != null && | |||
projects.length < projectsAppState.total; | |||
if (view === 'visualizations' && !limitReached) { | |||
return ( | |||
<Tooltip overlay={translate('projects.sort.disabled')}> | |||
<div> | |||
<ProjectsSortingSelect | |||
className="projects-topbar-item js-projects-sorting-select disabled" | |||
onChange={this.props.onSortChange} | |||
selectedSort={this.props.selectedSort} | |||
view={this.props.view} | |||
/> | |||
</div> | |||
</Tooltip> | |||
); | |||
} | |||
return ( | |||
<ProjectsSortingSelect | |||
className="projects-topbar-item js-projects-sorting-select" | |||
onChange={this.props.onSortChange} | |||
selectedSort={this.props.selectedSort} | |||
view={this.props.view} | |||
/> | |||
); | |||
} | |||
render() { | |||
const { open } = this.props; | |||
return ( | |||
@@ -58,12 +93,7 @@ export default class ProjectsOptionBar extends React.PureComponent { | |||
view={this.props.view} | |||
visualization={this.props.visualization} | |||
/> | |||
<ProjectsSortingSelect | |||
className="projects-topbar-item js-projects-sorting-select" | |||
onChange={this.props.onSortChange} | |||
selectedSort={this.props.selectedSort} | |||
view={this.props.view} | |||
/> | |||
{this.renderSortingSelect()} | |||
</div> | |||
</div> | |||
</div> |
@@ -0,0 +1,29 @@ | |||
/* | |||
* 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 { connect } from 'react-redux'; | |||
import ProjectsOptionBar from './ProjectsOptionBar'; | |||
import { getProjects, getProjectsAppState } from '../../../store/rootReducer'; | |||
const mapStateToProps = state => ({ | |||
projects: getProjects(state), | |||
projectsAppState: getProjectsAppState(state) | |||
}); | |||
export default connect(mapStateToProps)(ProjectsOptionBar); |
@@ -28,11 +28,19 @@ it('should render option bar closed', () => { | |||
it('should render option bar open', () => { | |||
expect( | |||
shallow(<ProjectsOptionBar open={true} view="leak" visualization="risk" />) | |||
shallow( | |||
<ProjectsOptionBar | |||
open={true} | |||
view="leak" | |||
visualization="risk" | |||
projects={[1, 2, 3]} | |||
projectsAppState={{ total: 3 }} | |||
/> | |||
) | |||
).toMatchSnapshot(); | |||
}); | |||
it.skip('should not render sorting options for visualizations', () => { | |||
it('should render disabled sorting options for visualizations', () => { | |||
expect( | |||
shallow(<ProjectsOptionBar open={true} view="visualizations" visualization="coverage" />) | |||
).toMatchSnapshot(); |
@@ -8,7 +8,7 @@ Array [ | |||
] | |||
`; | |||
exports[`should not render sorting options for visualizations 1`] = ` | |||
exports[`should render disabled sorting options for visualizations 1`] = ` | |||
<div | |||
className="projects-topbar" | |||
> | |||
@@ -26,10 +26,21 @@ exports[`should not render sorting options for visualizations 1`] = ` | |||
className="projects-topbar-actions-inner" | |||
> | |||
<PerspectiveSelect | |||
className="projects-topbar-item" | |||
className="projects-topbar-item js-projects-perspective-select" | |||
view="visualizations" | |||
visualization="coverage" | |||
/> | |||
<Tooltip | |||
overlay="projects.sort.disabled" | |||
placement="bottom" | |||
> | |||
<div> | |||
<ProjectsSortingSelect | |||
className="projects-topbar-item js-projects-sorting-select disabled" | |||
view="visualizations" | |||
/> | |||
</div> | |||
</Tooltip> | |||
</div> | |||
</div> | |||
</div> |
@@ -43,6 +43,15 @@ | |||
padding: 0 24px; | |||
} | |||
.projects-topbar-item.disabled { | |||
cursor: not-allowed; | |||
opacity: 0.4; | |||
} | |||
.projects-topbar-item.disabled * { | |||
pointer-events: none !important; | |||
} | |||
.projects-topbar-button { | |||
box-sizing: border-box; | |||
float: right; |
@@ -910,6 +910,7 @@ projects.facets.quality_gate=Quality Gate | |||
projects.facets.languages=Languages | |||
projects.facets.new_lines=New Lines | |||
projects.facets.tags=Tags | |||
projects.sort.disabled=Disabled because sorting cannot affect the displayed result with the current project selection. | |||
projects.sort.name=by name | |||
projects.sort.reliability=by reliability (best first) | |||
projects.sort.-reliability=by reliability (worst first) |