Browse Source

SONAR-10081 Add Explore space for SonarCloud

tags/7.0-RC1
Stas Vilchik 6 years ago
parent
commit
5038ceecaa
85 changed files with 684 additions and 662 deletions
  1. 25
    18
      server/sonar-web/src/main/js/app/components/Landing.tsx
  2. 2
    2
      server/sonar-web/src/main/js/app/components/help/GlobalHelp.js
  3. 1
    1
      server/sonar-web/src/main/js/app/components/help/__tests__/GlobalHelp-test.js
  4. 7
    3
      server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.js
  5. 45
    0
      server/sonar-web/src/main/js/app/components/nav/global/GlobalNavExplore.tsx
  6. 12
    3
      server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.js
  7. 7
    0
      server/sonar-web/src/main/js/app/utils/startReactApp.js
  8. 57
    0
      server/sonar-web/src/main/js/apps/explore/Explore.tsx
  9. 30
    0
      server/sonar-web/src/main/js/apps/explore/ExploreIssues.tsx
  10. 30
    0
      server/sonar-web/src/main/js/apps/explore/ExploreProjects.tsx
  11. 7
    2
      server/sonar-web/src/main/js/apps/issues/IssuesPageSelector.tsx
  12. 3
    1
      server/sonar-web/src/main/js/apps/issues/components/App.js
  13. 1
    0
      server/sonar-web/src/main/js/apps/issues/components/AppContainer.tsx
  14. 30
    0
      server/sonar-web/src/main/js/apps/issues/components/NoMyIssues.tsx
  15. 30
    27
      server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx
  16. 10
    1
      server/sonar-web/src/main/js/apps/projects/components/AllProjectsContainer.tsx
  17. 43
    0
      server/sonar-web/src/main/js/apps/projects/components/ClearAll.tsx
  18. 13
    4
      server/sonar-web/src/main/js/apps/projects/components/DefaultPageSelector.tsx
  19. 6
    2
      server/sonar-web/src/main/js/apps/projects/components/NoFavoriteProjects.tsx
  20. 2
    2
      server/sonar-web/src/main/js/apps/projects/components/PageHeader.tsx
  21. 6
    13
      server/sonar-web/src/main/js/apps/projects/components/PageSidebar.tsx
  22. 6
    1
      server/sonar-web/src/main/js/apps/projects/components/ProjectsList.tsx
  23. 34
    0
      server/sonar-web/src/main/js/apps/projects/components/__tests__/ClearAll-test.tsx
  24. 1
    1
      server/sonar-web/src/main/js/apps/projects/components/__tests__/NoFavoriteProjects-test.tsx
  25. 1
    0
      server/sonar-web/src/main/js/apps/projects/components/__tests__/PageHeader-test.tsx
  26. 8
    5
      server/sonar-web/src/main/js/apps/projects/components/__tests__/PageSidebar-test.tsx
  27. 9
    84
      server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/AllProjects-test.tsx.snap
  28. 14
    0
      server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ClearAll-test.tsx.snap
  29. 3
    0
      server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageHeader-test.tsx.snap
  30. 21
    58
      server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/PageSidebar-test.tsx.snap
  31. 3
    2
      server/sonar-web/src/main/js/apps/projects/filters/CoverageFilter.tsx
  32. 3
    2
      server/sonar-web/src/main/js/apps/projects/filters/DuplicationsFilter.tsx
  33. 20
    22
      server/sonar-web/src/main/js/apps/projects/filters/Filter.tsx
  34. 3
    2
      server/sonar-web/src/main/js/apps/projects/filters/IssuesFilter.tsx
  35. 4
    3
      server/sonar-web/src/main/js/apps/projects/filters/LanguagesFilter.tsx
  36. 2
    1
      server/sonar-web/src/main/js/apps/projects/filters/MaintainabilityFilter.tsx
  37. 3
    2
      server/sonar-web/src/main/js/apps/projects/filters/NewLinesFilter.tsx
  38. 2
    1
      server/sonar-web/src/main/js/apps/projects/filters/NewMaintainabilityFilter.tsx
  39. 2
    1
      server/sonar-web/src/main/js/apps/projects/filters/NewReliabilityFilter.tsx
  40. 2
    1
      server/sonar-web/src/main/js/apps/projects/filters/NewSecurityFilter.tsx
  41. 4
    3
      server/sonar-web/src/main/js/apps/projects/filters/QualityGateFilter.tsx
  42. 2
    1
      server/sonar-web/src/main/js/apps/projects/filters/ReliabilityFilter.tsx
  43. 3
    9
      server/sonar-web/src/main/js/apps/projects/filters/SearchFilterContainer.tsx
  44. 3
    8
      server/sonar-web/src/main/js/apps/projects/filters/SearchableFilterFooter.tsx
  45. 2
    1
      server/sonar-web/src/main/js/apps/projects/filters/SecurityFilter.tsx
  46. 3
    2
      server/sonar-web/src/main/js/apps/projects/filters/SizeFilter.tsx
  47. 4
    3
      server/sonar-web/src/main/js/apps/projects/filters/TagsFilter.tsx
  48. 1
    1
      server/sonar-web/src/main/js/apps/projects/filters/__tests__/CoverageFilter-test.tsx
  49. 1
    1
      server/sonar-web/src/main/js/apps/projects/filters/__tests__/DuplicationsFilter-test.tsx
  50. 1
    0
      server/sonar-web/src/main/js/apps/projects/filters/__tests__/Filter-test.tsx
  51. 3
    1
      server/sonar-web/src/main/js/apps/projects/filters/__tests__/IssuesFilter-test.tsx
  52. 8
    3
      server/sonar-web/src/main/js/apps/projects/filters/__tests__/LanguagesFilter-test.tsx
  53. 1
    1
      server/sonar-web/src/main/js/apps/projects/filters/__tests__/MaintainabilityFilter-test.tsx
  54. 1
    1
      server/sonar-web/src/main/js/apps/projects/filters/__tests__/NewCoverageFilter-test.tsx
  55. 1
    1
      server/sonar-web/src/main/js/apps/projects/filters/__tests__/NewDuplicationsFilter-test.tsx
  56. 1
    1
      server/sonar-web/src/main/js/apps/projects/filters/__tests__/NewLinesFilter-test.tsx
  57. 3
    1
      server/sonar-web/src/main/js/apps/projects/filters/__tests__/NewMaintainabilityFilter-test.tsx
  58. 1
    1
      server/sonar-web/src/main/js/apps/projects/filters/__tests__/NewReliabilityFilter-test.tsx
  59. 1
    1
      server/sonar-web/src/main/js/apps/projects/filters/__tests__/NewSecurityFilter-test.tsx
  60. 1
    1
      server/sonar-web/src/main/js/apps/projects/filters/__tests__/QualityGateFilter-test.tsx
  61. 1
    1
      server/sonar-web/src/main/js/apps/projects/filters/__tests__/ReliabilityFilter-test.tsx
  62. 5
    3
      server/sonar-web/src/main/js/apps/projects/filters/__tests__/SearchFilterContainer-test.tsx
  63. 5
    6
      server/sonar-web/src/main/js/apps/projects/filters/__tests__/SearchableFilterFooter-test.tsx
  64. 1
    1
      server/sonar-web/src/main/js/apps/projects/filters/__tests__/SecurityFilter-test.tsx
  65. 1
    1
      server/sonar-web/src/main/js/apps/projects/filters/__tests__/SizeFilter-test.tsx
  66. 5
    3
      server/sonar-web/src/main/js/apps/projects/filters/__tests__/TagsFilter-test.tsx
  67. 1
    0
      server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/CoverageFilter-test.tsx.snap
  68. 1
    0
      server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/DuplicationsFilter-test.tsx.snap
  69. 63
    248
      server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/Filter-test.tsx.snap
  70. 1
    0
      server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/IssuesFilter-test.tsx.snap
  71. 16
    42
      server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/LanguagesFilter-test.tsx.snap
  72. 1
    0
      server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/MaintainabilityFilter-test.tsx.snap
  73. 1
    0
      server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/NewCoverageFilter-test.tsx.snap
  74. 1
    0
      server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/NewDuplicationsFilter-test.tsx.snap
  75. 1
    0
      server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/NewLinesFilter-test.tsx.snap
  76. 1
    0
      server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/NewMaintainabilityFilter-test.tsx.snap
  77. 1
    0
      server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/NewReliabilityFilter-test.tsx.snap
  78. 1
    0
      server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/NewSecurityFilter-test.tsx.snap
  79. 1
    0
      server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/QualityGateFilter-test.tsx.snap
  80. 1
    0
      server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/ReliabilityFilter-test.tsx.snap
  81. 1
    0
      server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/SecurityFilter-test.tsx.snap
  82. 1
    0
      server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/SizeFilter-test.tsx.snap
  83. 18
    43
      server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/TagsFilter-test.tsx.snap
  84. 0
    8
      server/sonar-web/src/main/js/apps/projects/styles.css
  85. 2
    0
      sonar-core/src/main/resources/org/sonar/l10n/core.properties

server/sonar-web/src/main/js/app/components/Landing.js → server/sonar-web/src/main/js/app/components/Landing.tsx View File

@@ -17,26 +17,30 @@
* 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 PropTypes from 'prop-types';
import { withRouter } from 'react-router';
import * as React from 'react';
import * as PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { getCurrentUser, getGlobalSettingValue } from '../../store/rootReducer';
import { CurrentUser, isLoggedIn } from '../types';

class Landing extends React.PureComponent {
static propTypes = {
currentUser: PropTypes.oneOfType([PropTypes.bool, PropTypes.object]).isRequired
interface Props {
currentUser: CurrentUser;
onSonarCloud: boolean;
}

class Landing extends React.PureComponent<Props> {
static contextTypes = {
router: PropTypes.object.isRequired
};

componentDidMount() {
const { currentUser, router, onSonarCloud } = this.props;
if (currentUser.isLoggedIn) {
router.replace('/projects');
} else if (onSonarCloud && onSonarCloud.value === 'true') {
window.location = 'https://about.sonarcloud.io';
const { currentUser, onSonarCloud } = this.props;
if (isLoggedIn(currentUser)) {
this.context.router.replace('/projects');
} else if (onSonarCloud) {
window.location.href = 'https://about.sonarcloud.io';
} else {
router.replace('/about');
this.context.router.replace('/about');
}
}

@@ -45,9 +49,12 @@ class Landing extends React.PureComponent {
}
}

const mapStateToProps = state => ({
currentUser: getCurrentUser(state),
onSonarCloud: getGlobalSettingValue(state, 'sonar.sonarcloud.enabled')
});
const mapStateToProps = (state: any) => {
const onSonarCloudSetting = getGlobalSettingValue(state, 'sonar.sonarcloud.enabled');
return {
currentUser: getCurrentUser(state),
onSonarCloud: Boolean(onSonarCloudSetting && onSonarCloudSetting.value === 'true')
};
};

export default connect(mapStateToProps)(withRouter(Landing));
export default connect<Props>(mapStateToProps)(Landing);

+ 2
- 2
server/sonar-web/src/main/js/app/components/help/GlobalHelp.js View File

@@ -32,7 +32,7 @@ type Props = {
currentUser: { isLoggedIn: boolean },
onClose: () => void,
onTutorialSelect: () => void,
sonarCloud?: boolean
onSonarCloud?: boolean
};
*/

@@ -62,7 +62,7 @@ export default class GlobalHelp extends React.PureComponent {
case 'shortcuts':
return <ShortcutsHelp />;
case 'links':
return this.props.sonarCloud ? (
return this.props.onSonarCloud ? (
<LinksHelpSonarCloud onClose={this.props.onClose} />
) : (
<LinksHelp onClose={this.props.onClose} />

+ 1
- 1
server/sonar-web/src/main/js/app/components/help/__tests__/GlobalHelp-test.js View File

@@ -58,7 +58,7 @@ it('display special links page for SonarCloud', () => {
currentUser={{ isLoggedIn: false }}
onClose={jest.fn()}
onTutorialSelect={jest.fn()}
sonarCloud={true}
onSonarCloud={true}
/>
);
clickOnSection(wrapper, 'links');

+ 7
- 3
server/sonar-web/src/main/js/app/components/nav/global/GlobalNav.js View File

@@ -22,6 +22,7 @@ import React from 'react';
import { connect } from 'react-redux';
import GlobalNavBranding from './GlobalNavBranding';
import GlobalNavMenu from './GlobalNavMenu';
import GlobalNavExplore from './GlobalNavExplore';
import GlobalNavUserContainer from './GlobalNavUserContainer';
import Search from '../../search/Search';
import GlobalHelp from '../../help/GlobalHelp';
@@ -38,8 +39,9 @@ import './GlobalNav.css';
type Props = {
appState: { organizationsEnabled: boolean },
currentUser: { isLoggedIn: boolean, showOnboardingTutorial: boolean },
location: { pathname: string },
skipOnboarding: () => void,
sonarCloud: boolean
onSonarCloud: boolean
};
*/

@@ -129,12 +131,14 @@ class GlobalNav extends React.PureComponent {
<GlobalNavUserContainer {...this.props} />
</ul>

<GlobalNavExplore location={this.props.location} onSonarCloud={this.props.onSonarCloud} />

{this.state.helpOpen && (
<GlobalHelp
currentUser={this.props.currentUser}
onClose={this.closeHelp}
onTutorialSelect={this.openOnboardingTutorial}
sonarCloud={this.props.sonarCloud}
onSonarCloud={this.props.onSonarCloud}
/>
)}

@@ -152,7 +156,7 @@ const mapStateToProps = state => {
return {
currentUser: getCurrentUser(state),
appState: getAppState(state),
sonarCloud: sonarCloudSetting != null && sonarCloudSetting.value === 'true'
onSonarCloud: Boolean(sonarCloudSetting && sonarCloudSetting.value === 'true')
};
};


+ 45
- 0
server/sonar-web/src/main/js/app/components/nav/global/GlobalNavExplore.tsx View File

@@ -0,0 +1,45 @@
/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:contact AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import { Link } from 'react-router';
import { translate } from '../../../../helpers/l10n';

interface Props {
location: { pathname: string };
onSonarCloud: boolean;
}

export default function GlobalNavExplore({ location, onSonarCloud }: Props) {
if (!onSonarCloud) {
return null;
}

const active = location.pathname.startsWith('explore');

return (
<ul className="global-navbar-menu spacer-right pull-right">
<li>
<Link to="/explore/projects" className={active ? 'active' : undefined}>
{translate('explore')}
</Link>
</li>
</ul>
);
}

+ 12
- 3
server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.js View File

@@ -20,6 +20,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router';
import { isLoggedIn } from '../../../../app/types';
import { translate } from '../../../../helpers/l10n';
import { getQualityGatesUrl } from '../../../../helpers/urls';
import { isMySet } from '../../../../apps/issues/utils';
@@ -31,7 +32,7 @@ export default class GlobalNavMenu extends React.PureComponent {
location: PropTypes.shape({
pathname: PropTypes.string.isRequired
}).isRequired,
sonarCloud: PropTypes.bool
onSonarCloud: PropTypes.bool
};

static defaultProps = {
@@ -44,10 +45,14 @@ export default class GlobalNavMenu extends React.PureComponent {
}

renderProjects() {
if (this.props.onSonarCloud && !isLoggedIn(this.props.currentUser)) {
return null;
}

return (
<li>
<Link to="/projects" activeClassName="active">
{this.props.sonarCloud ? translate('my_projects') : translate('projects.page')}
{this.props.onSonarCloud ? translate('my_projects') : translate('projects.page')}
</Link>
</li>
);
@@ -64,9 +69,13 @@ export default class GlobalNavMenu extends React.PureComponent {
}

renderIssuesLink() {
if (this.props.onSonarCloud && !isLoggedIn(this.props.currentUser)) {
return null;
}

const active = this.props.location.pathname === 'issues';

if (this.props.sonarCloud) {
if (this.props.onSonarCloud) {
return (
<li>
<Link

+ 7
- 0
server/sonar-web/src/main/js/app/utils/startReactApp.js View File

@@ -48,6 +48,9 @@ import componentMeasuresRoutes from '../../apps/component-measures/routes';
import customMeasuresRoutes from '../../apps/custom-measures/routes';
import groupsRoutes from '../../apps/groups/routes';
import Issues from '../../apps/issues/components/AppContainer';
import Explore from '../../apps/explore/Explore';
import ExploreIssues from '../../apps/explore/ExploreIssues';
import ExploreProjects from '../../apps/explore/ExploreProjects';
import IssuesPageSelector from '../../apps/issues/IssuesPageSelector';
import marketplaceRoutes from '../../apps/marketplace/routes';
import metricsRoutes from '../../apps/metrics/routes';
@@ -164,6 +167,10 @@ const startReactApp = () => {
<Route path="account" childRoutes={accountRoutes} />
<Route path="coding_rules" childRoutes={codingRulesRoutes} />
<Route path="component" childRoutes={componentRoutes} />
<Route path="explore" component={Explore}>
<Route path="issues" component={ExploreIssues} />
<Route path="projects" component={ExploreProjects} />
</Route>
<Route
path="extension/:pluginKey/:extensionKey"
component={GlobalPageExtension}

+ 57
- 0
server/sonar-web/src/main/js/apps/explore/Explore.tsx View File

@@ -0,0 +1,57 @@
/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:contact AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import { Link } from 'react-router';
import ContextNavBar from '../../components/nav/ContextNavBar';
import NavBarTabs from '../../components/nav/NavBarTabs';
import { translate } from '../../helpers/l10n';

interface Props {
children: JSX.Element;
}

export default function Explore(props: Props) {
return (
<div id="explore">
<ContextNavBar id="explore-navigation" height={65}>
<div className="navbar-context-header">
<h1 className="display-inline-block">{translate('explore')}</h1>
</div>

<NavBarTabs>
<li>
<Link to="/explore/projects" activeClassName="active">
{translate('projects.page')}
</Link>
</li>
<li>
<Link
to={{ pathname: '/explore/issues', query: { resolved: 'false' } }}
activeClassName="active">
{translate('issues.page')}
</Link>
</li>
</NavBarTabs>
</ContextNavBar>

{props.children}
</div>
);
}

+ 30
- 0
server/sonar-web/src/main/js/apps/explore/ExploreIssues.tsx View File

@@ -0,0 +1,30 @@
/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:contact AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import AppContainer from '../issues/components/AppContainer';
import { RawQuery } from '../../helpers/query';

interface Props {
location: { pathname: string; query: RawQuery };
}

export default function ExploreIssues(props: Props) {
return <AppContainer myIssues={false} {...props} />;
}

+ 30
- 0
server/sonar-web/src/main/js/apps/explore/ExploreProjects.tsx View File

@@ -0,0 +1,30 @@
/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:contact AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import AllProjectsContainer from '../projects/components/AllProjectsContainer';
import { RawQuery } from '../../helpers/query';

interface Props {
location: { pathname: string; query: RawQuery };
}

export default function ExploreProjects(props: Props) {
return <AllProjectsContainer isFavorite={false} {...props} />;
}

+ 7
- 2
server/sonar-web/src/main/js/apps/issues/IssuesPageSelector.tsx View File

@@ -22,15 +22,20 @@ import { connect } from 'react-redux';
import AppContainer from './components/AppContainer';
import { CurrentUser, isLoggedIn } from '../../app/types';
import { getCurrentUser, getGlobalSettingValue } from '../../store/rootReducer';
import { RawQuery } from '../../helpers/query';

interface StateProps {
currentUser: CurrentUser;
onSonarCloud: boolean;
}

function IssuesPage({ currentUser, onSonarCloud, ...props }: StateProps) {
interface Props extends StateProps {
location: { pathname: string; query: RawQuery };
}

function IssuesPage({ currentUser, location, onSonarCloud }: Props) {
const myIssues = (isLoggedIn(currentUser) && onSonarCloud) || undefined;
return <AppContainer myIssues={myIssues} {...props} />;
return <AppContainer location={location} myIssues={myIssues} />;
}

const stateToProps = (state: any) => {

+ 3
- 1
server/sonar-web/src/main/js/apps/issues/components/App.js View File

@@ -31,6 +31,7 @@ import IssuesList from './IssuesList';
import ComponentBreadcrumbs from './ComponentBreadcrumbs';
import IssuesSourceViewer from './IssuesSourceViewer';
import BulkChangeModal from './BulkChangeModal';
import NoMyIssues from './NoMyIssues';
import ConciseIssuesList from '../conciseIssuesList/ConciseIssuesList';
import ConciseIssuesListHeader from '../conciseIssuesList/ConciseIssuesListHeader';
import * as actions from '../actions';
@@ -868,7 +869,8 @@ export default class App extends React.PureComponent {
<ListFooter total={paging.total} count={issues.length} loadMore={this.fetchMoreIssues} />
)}

{paging.total === 0 && <EmptySearch />}
{paging.total === 0 &&
(this.state.myIssues && !this.isFiltered() ? <NoMyIssues /> : <EmptySearch />)}
</div>
);
}

+ 1
- 0
server/sonar-web/src/main/js/apps/issues/components/AppContainer.tsx View File

@@ -88,6 +88,7 @@ interface DispatchProps {
const mapDispatchToProps = { fetchIssues: fetchIssues as any } as DispatchProps;

interface OwnProps {
location: { pathname: string; query: RawQuery };
myIssues?: boolean;
}


+ 30
- 0
server/sonar-web/src/main/js/apps/issues/components/NoMyIssues.tsx View File

@@ -0,0 +1,30 @@
/*
* 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 * as React from 'react';
import { translate } from '../../../helpers/l10n';
import '../../../components/common/EmptySearch.css';

export default function NoMyIssues() {
return (
<div className="empty-search">
<h3>{translate('issues.no_my_issues')}</h3>
</div>
);
}

+ 30
- 27
server/sonar-web/src/main/js/apps/projects/components/AllProjects.tsx View File

@@ -20,12 +20,14 @@
import * as React from 'react';
import * as PropTypes from 'prop-types';
import Helmet from 'react-helmet';
import { omitBy } from 'lodash';
import PageHeader from './PageHeader';
import ProjectsList from './ProjectsList';
import PageSidebar from './PageSidebar';
import Visualizations from '../visualizations/Visualizations';
import { CurrentUser, isLoggedIn } from '../../../app/types';
import handleRequiredAuthentication from '../../../app/utils/handleRequiredAuthentication';
import ScreenPositionHelper from '../../../components/common/ScreenPositionHelper';
import ListFooter from '../../../components/controls/ListFooter';
import { translate } from '../../../helpers/l10n';
import * as storage from '../../../helpers/storage';
@@ -38,7 +40,7 @@ import { parseUrlQuery, Query } from '../query';
export interface Props {
currentUser: CurrentUser;
isFavorite: boolean;
location: { pathname: string; query: { [x: string]: string } };
location: { pathname: string; query: RawQuery };
onSonarCloud: boolean;
organization?: { key: string };
organizationsEnabled: boolean;
@@ -224,35 +226,35 @@ export default class AllProjects extends React.PureComponent<Props, State> {
}

updateLocationQuery = (newQuery: RawQuery) => {
this.context.router.push({
pathname: this.props.location.pathname,
query: {
...this.props.location.query,
...newQuery
}
});
const query = omitBy({ ...this.props.location.query, ...newQuery }, x => !x);
this.context.router.push({ pathname: this.props.location.pathname, query });
};

handleClearAll = () => {
this.context.router.push({ pathname: this.props.location.pathname });
};

renderSide = () => (
<div className="layout-page-side-outer">
<div
className="layout-page-side projects-page-side"
style={{ top: this.props.organization ? 95 : 30 }}>
<div className="layout-page-side-inner">
<div className="layout-page-filters">
<PageSidebar
facets={this.state.facets}
isFavorite={this.props.isFavorite}
organization={this.props.organization}
query={this.state.query}
showFavoriteFilter={!this.props.onSonarCloud}
view={this.getView()}
visualization={this.getVisualization()}
/>
<ScreenPositionHelper className="layout-page-side-outer">
{({ top }) => (
<div className="layout-page-side projects-page-side" style={{ top }}>
<div className="layout-page-side-inner">
<div className="layout-page-filters">
<PageSidebar
facets={this.state.facets}
onClearAll={this.handleClearAll}
onQueryChange={this.updateLocationQuery}
organization={this.props.organization}
query={this.state.query}
showFavoriteFilter={!this.props.onSonarCloud}
view={this.getView()}
visualization={this.getVisualization()}
/>
</div>
</div>
</div>
</div>
</div>
)}
</ScreenPositionHelper>
);

renderHeader = () => (
@@ -261,9 +263,9 @@ export default class AllProjects extends React.PureComponent<Props, State> {
<div className="layout-page-main-inner">
<PageHeader
currentUser={this.props.currentUser}
isFavorite={this.props.isFavorite}
loading={this.state.loading}
onPerspectiveChange={this.handlePerspectiveChange}
onQueryChange={this.updateLocationQuery}
onSortChange={this.handleSortChange}
organization={this.props.organization}
projects={this.state.projects}
@@ -298,6 +300,7 @@ export default class AllProjects extends React.PureComponent<Props, State> {
cardType={this.getView()}
isFavorite={this.props.isFavorite}
isFiltered={this.isFiltered()}
onSonarCloud={this.props.onSonarCloud}
organization={this.props.organization}
projects={this.state.projects}
query={this.state.query}
@@ -319,7 +322,7 @@ export default class AllProjects extends React.PureComponent<Props, State> {

{this.renderSide()}

<div className="layout-page-main projects-page-content">
<div className="layout-page-main">
{this.renderHeader()}
{this.renderMain()}
</div>

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

@@ -25,6 +25,7 @@ import {
areThereCustomOrganizations,
getGlobalSettingValue
} from '../../../store/rootReducer';
import { RawQuery } from '../../../helpers/query';

interface StateProps {
currentUser: CurrentUser;
@@ -32,6 +33,12 @@ interface StateProps {
organizationsEnabled: boolean;
}

interface OwnProps {
isFavorite: boolean;
location: { pathname: string; query: RawQuery };
organization?: { key: string };
}

const stateToProps = (state: any) => {
const onSonarCloudSetting = getGlobalSettingValue(state, 'sonar.sonarcloud.enabled');
return {
@@ -41,4 +48,6 @@ const stateToProps = (state: any) => {
};
};

export default connect<StateProps, any, any>(stateToProps)(lazyLoad(() => import('./AllProjects')));
export default connect<StateProps, {}, OwnProps>(stateToProps)(
lazyLoad(() => import('./AllProjects'))
);

+ 43
- 0
server/sonar-web/src/main/js/apps/projects/components/ClearAll.tsx View File

@@ -0,0 +1,43 @@
/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:contact AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import { translate } from '../../../helpers/l10n';

interface Props {
onClearAll: () => void;
}

export default class ClearAll extends React.PureComponent<Props> {
handleClick = (event: React.SyntheticEvent<HTMLButtonElement>) => {
event.preventDefault();
event.currentTarget.blur();
this.props.onClearAll();
};

render() {
return (
<div className="projects-facets-reset">
<button className="button-red" onClick={this.handleClick}>
{translate('clear_all_filters')}
</button>
</div>
);
}
}

+ 13
- 4
server/sonar-web/src/main/js/apps/projects/components/DefaultPageSelector.tsx View File

@@ -46,6 +46,10 @@ export default class DefaultPageSelector extends React.PureComponent<Props, Stat
}

componentDidMount() {
if (this.props.onSonarCloud && !isLoggedIn(this.props.currentUser)) {
this.context.router.replace('/explore/projects');
}

if (!this.props.onSonarCloud) {
this.defineIfShouldBeRedirected();
}
@@ -98,15 +102,20 @@ export default class DefaultPageSelector extends React.PureComponent<Props, Stat
}

render() {
if (this.props.onSonarCloud) {
if (this.props.onSonarCloud && isLoggedIn(this.props.currentUser)) {
return <AllProjectsContainer isFavorite={true} location={this.props.location} />;
}

const { shouldBeRedirected, shouldForceSorting } = this.state;
if (shouldBeRedirected == null || shouldBeRedirected === true || shouldForceSorting != null) {
return null;
} else {

if (
shouldBeRedirected !== undefined &&
shouldBeRedirected !== true &&
shouldForceSorting === undefined
) {
return <AllProjectsContainer isFavorite={false} location={this.props.location} />;
}

return null;
}
}

+ 6
- 2
server/sonar-web/src/main/js/apps/projects/components/NoFavoriteProjects.tsx View File

@@ -21,13 +21,17 @@ import * as React from 'react';
import { Link } from 'react-router';
import { translate } from '../../../helpers/l10n';

export default function NoFavoriteProjects() {
interface Props {
onSonarCloud: boolean;
}

export default function NoFavoriteProjects({ onSonarCloud }: Props) {
return (
<div className="projects-empty-list">
<h3>{translate('projects.no_favorite_projects')}</h3>
<p className="big-spacer-top">{translate('projects.no_favorite_projects.engagement')}</p>
<p className="big-spacer-top">
<Link to="/projects/all" className="button">
<Link to={onSonarCloud ? '/explore/projects' : '/projects/all'} className="button">
{translate('projects.explore_projects')}
</Link>
</p>

+ 2
- 2
server/sonar-web/src/main/js/apps/projects/components/PageHeader.tsx View File

@@ -30,9 +30,9 @@ import { Project } from '../types';

interface Props {
currentUser: CurrentUser;
isFavorite?: boolean;
loading: boolean;
onPerspectiveChange: (x: { view: string; visualization?: string }) => void;
onQueryChange: (change: RawQuery) => void;
onSortChange: (sort: string, desc: boolean) => void;
organization?: { key: string };
projects?: Project[];
@@ -80,7 +80,7 @@ export default function PageHeader(props: Props) {
)}

<SearchFilterContainer
isFavorite={props.isFavorite}
onQueryChange={props.onQueryChange}
organization={props.organization}
query={props.query}
/>

+ 6
- 13
server/sonar-web/src/main/js/apps/projects/components/PageSidebar.tsx View File

@@ -18,9 +18,9 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import { Link } from 'react-router';
import { flatMap } from 'lodash';
import FavoriteFilterContainer from './FavoriteFilterContainer';
import ClearAll from './ClearAll';
import LanguagesFilterContainer from '../filters/LanguagesFilterContainer';
import CoverageFilter from '../filters/CoverageFilter';
import DuplicationsFilter from '../filters/DuplicationsFilter';
@@ -42,7 +42,8 @@ import { Facets } from '../types';

interface Props {
facets?: Facets;
isFavorite: boolean;
onClearAll: () => void;
onQueryChange: (change: RawQuery) => void;
organization?: { key: string };
query: RawQuery;
showFavoriteFilter: boolean;
@@ -51,15 +52,13 @@ interface Props {
}

export default function PageSidebar(props: Props) {
const { facets, query, isFavorite, organization, view, visualization } = props;
const { facets, onQueryChange, query, organization, view, visualization } = props;
const isFiltered = Object.keys(query)
.filter(key => !['view', 'visualization', 'sort'].includes(key))
.some(key => query[key] != null);
const isLeakView = view === 'leak';
const basePathName = organization ? `/organizations/${organization.key}/projects` : '/projects';
const pathname = basePathName + (isFavorite ? '/favorite' : '');
const maxFacetValue = getMaxFacetValue(facets);
const facetProps = { isFavorite, maxFacetValue, organization, query };
const facetProps = { onQueryChange, maxFacetValue, organization, query };

let linkQuery: RawQuery | undefined = undefined;
if (view !== 'overall') {
@@ -77,13 +76,7 @@ export default function PageSidebar(props: Props) {
)}

<div className="projects-facets-header clearfix">
{isFiltered && (
<div className="projects-facets-reset">
<Link to={{ pathname, query: linkQuery }} className="button button-red">
{translate('clear_all_filters')}
</Link>
</div>
)}
{isFiltered && <ClearAll onClearAll={props.onClearAll} />}

<h3>{translate('filters')}</h3>
</div>

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

@@ -30,6 +30,7 @@ interface Props {
cardType?: string;
isFavorite: boolean;
isFiltered: boolean;
onSonarCloud: boolean;
organization?: { key: string };
projects: Project[];
query: Query;
@@ -41,7 +42,11 @@ export default class ProjectsList extends React.PureComponent<Props> {
if (isFiltered) {
return isFavorite ? <EmptyFavoriteSearch query={query} /> : <EmptySearch />;
}
return isFavorite ? <NoFavoriteProjects /> : <EmptyInstance />;
return isFavorite ? (
<NoFavoriteProjects onSonarCloud={this.props.onSonarCloud} />
) : (
<EmptyInstance />
);
}

render() {

+ 34
- 0
server/sonar-web/src/main/js/apps/projects/components/__tests__/ClearAll-test.tsx View File

@@ -0,0 +1,34 @@
/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:contact AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import { shallow } from 'enzyme';
import ClearAll from '../ClearAll';
import { click } from '../../../../helpers/testUtils';

it('renders', () => {
expect(shallow(<ClearAll onClearAll={jest.fn()} />)).toMatchSnapshot();
});

it('clears all', () => {
const onClearAll = jest.fn();
const wrapper = shallow(<ClearAll onClearAll={onClearAll} />);
click(wrapper.find('button'));
expect(onClearAll).toBeCalled();
});

+ 1
- 1
server/sonar-web/src/main/js/apps/projects/components/__tests__/NoFavoriteProjects-test.tsx View File

@@ -22,5 +22,5 @@ import { shallow } from 'enzyme';
import NoFavoriteProjects from '../NoFavoriteProjects';

it('renders', () => {
expect(shallow(<NoFavoriteProjects />)).toMatchSnapshot();
expect(shallow(<NoFavoriteProjects onSonarCloud={false} />)).toMatchSnapshot();
});

+ 1
- 0
server/sonar-web/src/main/js/apps/projects/components/__tests__/PageHeader-test.tsx View File

@@ -73,6 +73,7 @@ function shallowRender(props?: {}) {
currentUser={{ isLoggedIn: false }}
loading={false}
onPerspectiveChange={jest.fn()}
onQueryChange={jest.fn()}
onSortChange={jest.fn()}
projects={[]}
query={{ search: 'test' }}

+ 8
- 5
server/sonar-web/src/main/js/apps/projects/components/__tests__/PageSidebar-test.tsx View File

@@ -24,7 +24,8 @@ import PageSidebar from '../PageSidebar';
it('should render correctly', () => {
const sidebar = shallow(
<PageSidebar
isFavorite={true}
onClearAll={jest.fn()}
onQueryChange={jest.fn()}
query={{ size: '3' }}
showFavoriteFilter={true}
view="overall"
@@ -37,7 +38,8 @@ it('should render correctly', () => {
it('should render `leak` view correctly', () => {
const sidebar = shallow(
<PageSidebar
isFavorite={false}
onClearAll={jest.fn()}
onQueryChange={jest.fn()}
query={{ view: 'leak' }}
showFavoriteFilter={true}
view="leak"
@@ -50,14 +52,15 @@ it('should render `leak` view correctly', () => {
it('reset function should work correctly with view and visualizations', () => {
const sidebar = shallow(
<PageSidebar
isFavorite={false}
onClearAll={jest.fn()}
onQueryChange={jest.fn()}
query={{ view: 'visualizations', visualization: 'bugs' }}
showFavoriteFilter={true}
view="visualizations"
visualization="bugs"
/>
);
expect(sidebar.find('.projects-facets-reset').exists()).toBeFalsy();
expect(sidebar.find('ClearAll').exists()).toBeFalsy();
sidebar.setProps({ query: { size: '3' } });
expect(sidebar.find('.projects-facets-reset')).toMatchSnapshot();
expect(sidebar.find('ClearAll').exists()).toBeTruthy();
});

+ 9
- 84
server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/AllProjects-test.tsx.snap View File

@@ -10,58 +10,11 @@ exports[`renders 1`] = `
encodeSpecialCharacters={true}
title="projects.page"
/>
<div
<ScreenPositionHelper
className="layout-page-side-outer"
>
<div
className="layout-page-side projects-page-side"
style={
Object {
"top": 30,
}
}
>
<div
className="layout-page-side-inner"
>
<div
className="layout-page-filters"
>
<PageSidebar
isFavorite={false}
query={
Object {
"coverage": undefined,
"duplications": undefined,
"gate": undefined,
"languages": undefined,
"maintainability": undefined,
"new_coverage": undefined,
"new_duplications": undefined,
"new_lines": undefined,
"new_maintainability": undefined,
"new_reliability": undefined,
"new_security": undefined,
"reliability": undefined,
"search": undefined,
"security": undefined,
"size": undefined,
"sort": undefined,
"tags": undefined,
"view": undefined,
"visualization": undefined,
}
}
showFavoriteFilter={true}
view="overall"
visualization="risk"
/>
</div>
</div>
</div>
</div>
/>
<div
className="layout-page-main projects-page-content"
className="layout-page-main"
>
<div
className="layout-page-header-panel layout-page-main-header"
@@ -78,9 +31,9 @@ exports[`renders 1`] = `
"isLoggedIn": true,
}
}
isFavorite={false}
loading={false}
onPerspectiveChange={[Function]}
onQueryChange={[Function]}
onSortChange={[Function]}
projects={
Array [
@@ -129,6 +82,7 @@ exports[`renders 1`] = `
cardType="overall"
isFavorite={false}
isFiltered={false}
onSonarCloud={false}
projects={
Array [
Object {
@@ -183,40 +137,11 @@ exports[`renders 2`] = `
encodeSpecialCharacters={true}
title="projects.page"
/>
<div
<ScreenPositionHelper
className="layout-page-side-outer"
>
<div
className="layout-page-side projects-page-side"
style={
Object {
"top": 30,
}
}
>
<div
className="layout-page-side-inner"
>
<div
className="layout-page-filters"
>
<PageSidebar
isFavorite={false}
query={
Object {
"view": "visualizations",
}
}
showFavoriteFilter={true}
view="visualizations"
visualization="risk"
/>
</div>
</div>
</div>
</div>
/>
<div
className="layout-page-main projects-page-content"
className="layout-page-main"
>
<div
className="layout-page-header-panel layout-page-main-header"
@@ -233,9 +158,9 @@ exports[`renders 2`] = `
"isLoggedIn": true,
}
}
isFavorite={false}
loading={false}
onPerspectiveChange={[Function]}
onQueryChange={[Function]}
onSortChange={[Function]}
projects={
Array [

+ 14
- 0
server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ClearAll-test.tsx.snap View File

@@ -0,0 +1,14 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`renders 1`] = `
<div
className="projects-facets-reset"
>
<button
className="button-red"
onClick={[Function]}
>
clear_all_filters
</button>
</div>
`;

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

@@ -17,6 +17,7 @@ exports[`should render correctly 1`] = `
view="overall"
/>
<SearchFilterContainer
onQueryChange={[Function]}
query={
Object {
"search": "test",
@@ -56,6 +57,7 @@ exports[`should render correctly while loading 1`] = `
view="overall"
/>
<SearchFilterContainer
onQueryChange={[Function]}
query={
Object {
"search": "test",
@@ -108,6 +110,7 @@ exports[`should render disabled sorting options for visualizations 1`] = `
</div>
</Tooltip>
<SearchFilterContainer
onQueryChange={[Function]}
query={
Object {
"search": "test",

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

@@ -1,28 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`reset function should work correctly with view and visualizations 1`] = `
<div
className="projects-facets-reset"
>
<Link
className="button button-red"
onlyActiveOnIndex={false}
style={Object {}}
to={
Object {
"pathname": "/projects",
"query": Object {
"view": "visualizations",
"visualization": "bugs",
},
}
}
>
clear_all_filters
</Link>
</div>
`;

exports[`should render \`leak\` view correctly 1`] = `
<div>
<Connect(FavoriteFilter)
@@ -40,7 +17,7 @@ exports[`should render \`leak\` view correctly 1`] = `
</h3>
</div>
<QualityGateFilter
isFavorite={false}
onQueryChange={[Function]}
query={
Object {
"view": "leak",
@@ -48,8 +25,8 @@ exports[`should render \`leak\` view correctly 1`] = `
}
/>
<NewReliabilityFilter
isFavorite={false}
key="new_reliability"
onQueryChange={[Function]}
query={
Object {
"view": "leak",
@@ -57,8 +34,8 @@ exports[`should render \`leak\` view correctly 1`] = `
}
/>
<NewSecurityFilter
isFavorite={false}
key="new_security"
onQueryChange={[Function]}
query={
Object {
"view": "leak",
@@ -66,8 +43,8 @@ exports[`should render \`leak\` view correctly 1`] = `
}
/>
<NewMaintainabilityFilter
isFavorite={false}
key="new_maintainability"
onQueryChange={[Function]}
query={
Object {
"view": "leak",
@@ -75,8 +52,8 @@ exports[`should render \`leak\` view correctly 1`] = `
}
/>
<NewCoverageFilter
isFavorite={false}
key="new_coverage"
onQueryChange={[Function]}
query={
Object {
"view": "leak",
@@ -84,8 +61,8 @@ exports[`should render \`leak\` view correctly 1`] = `
}
/>
<NewDuplicationsFilter
isFavorite={false}
key="new_duplications"
onQueryChange={[Function]}
query={
Object {
"view": "leak",
@@ -93,8 +70,8 @@ exports[`should render \`leak\` view correctly 1`] = `
}
/>
<NewLinesFilter
isFavorite={false}
key="new_lines"
onQueryChange={[Function]}
query={
Object {
"view": "leak",
@@ -102,7 +79,7 @@ exports[`should render \`leak\` view correctly 1`] = `
}
/>
<Connect(LanguagesFilter)
isFavorite={false}
onQueryChange={[Function]}
query={
Object {
"view": "leak",
@@ -110,7 +87,7 @@ exports[`should render \`leak\` view correctly 1`] = `
}
/>
<TagsFilter
isFavorite={false}
onQueryChange={[Function]}
query={
Object {
"view": "leak",
@@ -126,29 +103,15 @@ exports[`should render correctly 1`] = `
<div
className="projects-facets-header clearfix"
>
<div
className="projects-facets-reset"
>
<Link
className="button button-red"
onlyActiveOnIndex={false}
style={Object {}}
to={
Object {
"pathname": "/projects/favorite",
"query": undefined,
}
}
>
clear_all_filters
</Link>
</div>
<ClearAll
onClearAll={[Function]}
/>
<h3>
filters
</h3>
</div>
<QualityGateFilter
isFavorite={true}
onQueryChange={[Function]}
query={
Object {
"size": "3",
@@ -156,8 +119,8 @@ exports[`should render correctly 1`] = `
}
/>
<ReliabilityFilter
isFavorite={true}
key="reliability"
onQueryChange={[Function]}
query={
Object {
"size": "3",
@@ -165,8 +128,8 @@ exports[`should render correctly 1`] = `
}
/>
<SecurityFilter
isFavorite={true}
key="security"
onQueryChange={[Function]}
query={
Object {
"size": "3",
@@ -174,8 +137,8 @@ exports[`should render correctly 1`] = `
}
/>
<MaintainabilityFilter
isFavorite={true}
key="maintainability"
onQueryChange={[Function]}
query={
Object {
"size": "3",
@@ -183,8 +146,8 @@ exports[`should render correctly 1`] = `
}
/>
<CoverageFilter
isFavorite={true}
key="coverage"
onQueryChange={[Function]}
query={
Object {
"size": "3",
@@ -192,8 +155,8 @@ exports[`should render correctly 1`] = `
}
/>
<DuplicationsFilter
isFavorite={true}
key="duplications"
onQueryChange={[Function]}
query={
Object {
"size": "3",
@@ -201,8 +164,8 @@ exports[`should render correctly 1`] = `
}
/>
<SizeFilter
isFavorite={true}
key="size"
onQueryChange={[Function]}
query={
Object {
"size": "3",
@@ -211,7 +174,7 @@ exports[`should render correctly 1`] = `
value="3"
/>
<Connect(LanguagesFilter)
isFavorite={true}
onQueryChange={[Function]}
query={
Object {
"size": "3",
@@ -219,7 +182,7 @@ exports[`should render correctly 1`] = `
}
/>
<TagsFilter
isFavorite={true}
onQueryChange={[Function]}
query={
Object {
"size": "3",

+ 3
- 2
server/sonar-web/src/main/js/apps/projects/filters/CoverageFilter.tsx View File

@@ -24,12 +24,13 @@ import CoverageRating from '../../../components/ui/CoverageRating';
import { getCoverageRatingLabel, getCoverageRatingAverageValue } from '../../../helpers/ratings';
import { translate } from '../../../helpers/l10n';
import { Facet } from '../types';
import { RawQuery } from '../../../helpers/query';

export interface Props {
className?: string;
facet?: Facet;
isFavorite?: boolean;
maxFacetValue?: number;
onQueryChange: (change: RawQuery) => void;
organization?: { key: string };
property?: string;
query: { [x: string]: any };
@@ -43,13 +44,13 @@ export default function CoverageFilter(props: Props) {
<Filter
facet={props.facet}
maxFacetValue={props.maxFacetValue}
onQueryChange={props.onQueryChange}
value={props.value}
property={property}
className={props.className}
options={[1, 2, 3, 4, 5, 6]}
query={props.query}
renderOption={renderOption}
isFavorite={props.isFavorite}
organization={props.organization}
getFacetValueForOption={getFacetValueForOption}
highlightUnder={1}

+ 3
- 2
server/sonar-web/src/main/js/apps/projects/filters/DuplicationsFilter.tsx View File

@@ -27,12 +27,13 @@ import {
} from '../../../helpers/ratings';
import { translate } from '../../../helpers/l10n';
import { Facet } from '../types';
import { RawQuery } from '../../../helpers/query';

export interface Props {
className?: string;
facet?: Facet;
isFavorite?: boolean;
maxFacetValue?: number;
onQueryChange: (change: RawQuery) => void;
organization?: { key: string };
property?: string;
query: { [x: string]: any };
@@ -45,13 +46,13 @@ export default function DuplicationsFilter(props: Props) {
<Filter
facet={props.facet}
maxFacetValue={props.maxFacetValue}
onQueryChange={props.onQueryChange}
value={props.value}
property={property}
className={props.className}
options={[1, 2, 3, 4, 5, 6]}
query={props.query}
renderOption={renderOption}
isFavorite={props.isFavorite}
organization={props.organization}
getFacetValueForOption={getFacetValueForOption}
highlightUnder={1}

+ 20
- 22
server/sonar-web/src/main/js/apps/projects/filters/Filter.tsx View File

@@ -19,17 +19,17 @@
*/
import * as React from 'react';
import * as classNames from 'classnames';
import { Link } from 'react-router';
import { getFilterUrl } from './utils';
import { formatMeasure } from '../../../helpers/measures';
import { translate } from '../../../helpers/l10n';
import { Facet } from '../types';
import { RawQuery } from '../../../helpers/query';

export type Option = string | number;

interface Props {
property: string;
className?: string;
onQueryChange: (change: RawQuery) => void;
options: Option[];
query: { [x: string]: any };
renderOption: (option: Option, isSelected: boolean) => React.ReactNode;
@@ -38,7 +38,6 @@ interface Props {
facet?: Facet;
maxFacetValue?: number;
optionClassName?: string;
isFavorite?: boolean;
organization?: { key: string };

getFacetValueForOption?: (facet: Facet, option: Option) => void;
@@ -54,7 +53,7 @@ interface Props {
export default class Filter extends React.PureComponent<Props> {
isSelected(option: Option): boolean {
const { value } = this.props;
return Array.isArray(value) ? value.includes(option) : option === value;
return Array.isArray(value) ? value.includes(option) : String(option) === String(value);
}

highlightUnder(option?: Option): boolean {
@@ -66,23 +65,28 @@ export default class Filter extends React.PureComponent<Props> {
);
}

blurOnClick = (event: React.SyntheticEvent<HTMLElement>) => event.currentTarget.blur();
handleClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
event.preventDefault();
event.currentTarget.blur();

getPath(option: Option) {
const { property, value } = this.props;
const { key: option } = event.currentTarget.dataset;
let urlOption;

if (Array.isArray(value)) {
if (this.isSelected(option)) {
urlOption = value.length > 1 ? value.filter(val => val !== option).join(',') : null;
if (option) {
if (Array.isArray(value)) {
if (this.isSelected(option)) {
urlOption = value.length > 1 ? value.filter(val => val !== option).join(',') : null;
} else {
urlOption = value.concat(option).join(',');
}
} else {
urlOption = value.concat(option).join(',');
urlOption = this.isSelected(option) ? null : option;
}
} else {
urlOption = this.isSelected(option) ? null : option;
this.props.onQueryChange({ [property]: urlOption });
}
return getFilterUrl(this.props, { [property]: urlOption });
}
};

renderOptionBar(facetValue: number | undefined) {
if (facetValue === undefined || !this.props.maxFacetValue) {
@@ -111,7 +115,6 @@ export default class Filter extends React.PureComponent<Props> {
this.props.optionClassName
);

const path = this.getPath(option);
const facetValue =
facet && getFacetValueForOption ? getFacetValueForOption(facet, option) : undefined;

@@ -122,12 +125,7 @@ export default class Filter extends React.PureComponent<Props> {
option > value;

return (
<Link
key={option}
className={className}
to={path}
data-key={option}
onClick={this.blurOnClick}>
<a className={className} data-key={option} href="#" key={option} onClick={this.handleClick}>
<span className="facet-name">
{this.props.renderOption(option, this.isSelected(option) || isUnderSelectedOption)}
</span>
@@ -137,7 +135,7 @@ export default class Filter extends React.PureComponent<Props> {
{this.renderOptionBar(facetValue)}
</span>
)}
</Link>
</a>
);
}


+ 3
- 2
server/sonar-web/src/main/js/apps/projects/filters/IssuesFilter.tsx View File

@@ -23,14 +23,15 @@ import FilterHeader from './FilterHeader';
import Rating from '../../../components/ui/Rating';
import { translate } from '../../../helpers/l10n';
import { Facet } from '../types';
import { RawQuery } from '../../../helpers/query';

interface Props {
className?: string;
facet?: Facet;
headerDetail?: React.ReactNode;
isFavorite?: boolean;
maxFacetValue?: number;
name: string;
onQueryChange: (change: RawQuery) => void;
organization?: { key: string };
property: string;
query: { [x: string]: any };
@@ -42,13 +43,13 @@ export default function IssuesFilter(props: Props) {
<Filter
facet={props.facet}
maxFacetValue={props.maxFacetValue}
onQueryChange={props.onQueryChange}
value={props.value}
property={props.property}
className={props.className}
options={[1, 2, 3, 4, 5]}
query={props.query}
renderOption={renderOption}
isFavorite={props.isFavorite}
organization={props.organization}
getFacetValueForOption={getFacetValueForOption}
highlightUnder={1}

+ 4
- 3
server/sonar-web/src/main/js/apps/projects/filters/LanguagesFilter.tsx View File

@@ -26,12 +26,13 @@ import SearchableFilterOption from './SearchableFilterOption';
import { getLanguageByKey, Languages } from '../../../store/languages/reducer';
import { translate } from '../../../helpers/l10n';
import { Facet } from '../types';
import { RawQuery } from '../../../helpers/query';

interface Props {
facet?: Facet;
isFavorite?: boolean;
languages: Languages;
maxFacetValue?: number;
onQueryChange: (change: RawQuery) => void;
organization?: { key: string };
property?: string;
query: { [x: string]: any };
@@ -68,6 +69,7 @@ export default class LanguagesFilter extends React.Component<Props> {

return (
<Filter
onQueryChange={this.props.onQueryChange}
property={property}
options={this.getSortedOptions(this.props.facet)}
query={this.props.query}
@@ -75,13 +77,12 @@ export default class LanguagesFilter extends React.Component<Props> {
value={this.props.value}
facet={this.props.facet}
maxFacetValue={this.props.maxFacetValue}
isFavorite={this.props.isFavorite}
organization={this.props.organization}
getFacetValueForOption={this.getFacetValueForOption}
header={<FilterHeader name={translate('projects.facets.languages')} />}
footer={
<SearchableFilterFooter
isFavorite={this.props.isFavorite}
onQueryChange={this.props.onQueryChange}
organization={this.props.organization}
options={this.getSearchOptions()}
property={property}

+ 2
- 1
server/sonar-web/src/main/js/apps/projects/filters/MaintainabilityFilter.tsx View File

@@ -22,13 +22,14 @@ import IssuesFilter from './IssuesFilter';
import { Facet } from '../types';
import CodeSmellIcon from '../../../components/icons-components/CodeSmellIcon';
import { translate } from '../../../helpers/l10n';
import { RawQuery } from '../../../helpers/query';

interface Props {
className?: string;
facet?: Facet;
headerDetail?: React.ReactNode;
isFavorite?: boolean;
maxFacetValue?: number;
onQueryChange: (change: RawQuery) => void;
organization?: { key: string };
query: { [x: string]: any };
value?: any;

+ 3
- 2
server/sonar-web/src/main/js/apps/projects/filters/NewLinesFilter.tsx View File

@@ -23,12 +23,13 @@ import FilterHeader from './FilterHeader';
import { translate } from '../../../helpers/l10n';
import { getSizeRatingLabel } from '../../../helpers/ratings';
import { Facet } from '../types';
import { RawQuery } from '../../../helpers/query';

export interface Props {
className?: string;
facet?: Facet;
isFavorite?: boolean;
maxFacetValue?: number;
onQueryChange: (change: RawQuery) => void;
organization?: { key: string };
property?: string;
query: { [x: string]: any };
@@ -42,13 +43,13 @@ export default function NewLinesFilter(props: Props) {
<Filter
facet={props.facet}
maxFacetValue={props.maxFacetValue}
onQueryChange={props.onQueryChange}
value={props.value}
property={property}
className="leak-facet-box"
options={[1, 2, 3, 4, 5]}
query={props.query}
renderOption={renderOption}
isFavorite={props.isFavorite}
organization={props.organization}
getFacetValueForOption={getFacetValueForOption}
highlightUnder={1}

+ 2
- 1
server/sonar-web/src/main/js/apps/projects/filters/NewMaintainabilityFilter.tsx View File

@@ -22,12 +22,13 @@ import CodeSmellIcon from '../../../components/icons-components/CodeSmellIcon';
import IssuesFilter from './IssuesFilter';
import { translate } from '../../../helpers/l10n';
import { Facet } from '../types';
import { RawQuery } from '../../../helpers/query';

interface Props {
className?: string;
facet?: Facet;
isFavorite?: boolean;
maxFacetValue?: number;
onQueryChange: (change: RawQuery) => void;
organization?: { key: string };
query: { [x: string]: any };
value?: any;

+ 2
- 1
server/sonar-web/src/main/js/apps/projects/filters/NewReliabilityFilter.tsx View File

@@ -22,12 +22,13 @@ import BugIcon from '../../../components/icons-components/BugIcon';
import IssuesFilter from './IssuesFilter';
import { translate } from '../../../helpers/l10n';
import { Facet } from '../types';
import { RawQuery } from '../../../helpers/query';

interface Props {
className?: string;
facet?: Facet;
isFavorite?: boolean;
maxFacetValue?: number;
onQueryChange: (change: RawQuery) => void;
organization?: { key: string };
query: { [x: string]: any };
value?: any;

+ 2
- 1
server/sonar-web/src/main/js/apps/projects/filters/NewSecurityFilter.tsx View File

@@ -22,12 +22,13 @@ import VulnerabilityIcon from '../../../components/icons-components/Vulnerabilit
import IssuesFilter from './IssuesFilter';
import { translate } from '../../../helpers/l10n';
import { Facet } from '../types';
import { RawQuery } from '../../../helpers/query';

interface Props {
className?: string;
facet?: Facet;
isFavorite?: boolean;
maxFacetValue?: number;
onQueryChange: (change: RawQuery) => void;
organization?: { key: string };
query: { [x: string]: any };
value?: any;

+ 4
- 3
server/sonar-web/src/main/js/apps/projects/filters/QualityGateFilter.tsx View File

@@ -20,15 +20,16 @@
import * as React from 'react';
import Filter from './Filter';
import FilterHeader from './FilterHeader';
import { Facet } from '../types';
import Level from '../../../components/ui/Level';
import { translate } from '../../../helpers/l10n';
import { Facet } from '../types';
import { RawQuery } from '../../../helpers/query';

export interface Props {
className?: string;
facet?: Facet;
isFavorite?: boolean;
maxFacetValue?: number;
onQueryChange: (change: RawQuery) => void;
organization?: { key: string };
query: { [x: string]: any };
value?: any;
@@ -39,12 +40,12 @@ export default function QualityGateFilter(props: Props) {
<Filter
facet={props.facet}
maxFacetValue={props.maxFacetValue}
onQueryChange={props.onQueryChange}
value={props.value}
property="gate"
options={['OK', 'WARN', 'ERROR']}
query={props.query}
renderOption={renderOption}
isFavorite={props.isFavorite}
organization={props.organization}
getFacetValueForOption={getFacetValueForOption}
header={<FilterHeader name={translate('projects.facets.quality_gate')} />}

+ 2
- 1
server/sonar-web/src/main/js/apps/projects/filters/ReliabilityFilter.tsx View File

@@ -22,13 +22,14 @@ import IssuesFilter from './IssuesFilter';
import { Facet } from '../types';
import BugIcon from '../../../components/icons-components/BugIcon';
import { translate } from '../../../helpers/l10n';
import { RawQuery } from '../../../helpers/query';

interface Props {
className?: string;
facet?: Facet;
headerDetail?: React.ReactNode;
isFavorite?: boolean;
maxFacetValue?: number;
onQueryChange: (change: RawQuery) => void;
organization?: { key: string };
query: { [x: string]: any };
value?: any;

+ 3
- 9
server/sonar-web/src/main/js/apps/projects/filters/SearchFilterContainer.tsx View File

@@ -18,26 +18,20 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import * as PropTypes from 'prop-types';
import { getFilterUrl } from './utils';
import SearchBox from '../../../components/controls/SearchBox';
import { translate } from '../../../helpers/l10n';
import { RawQuery } from '../../../helpers/query';

interface Props {
className?: string;
query: { search?: string };
isFavorite?: boolean;
onQueryChange: (change: RawQuery) => void;
organization?: { key: string };
}

export default class SearchFilterContainer extends React.PureComponent<Props> {
static contextTypes = {
router: PropTypes.object.isRequired
};

handleSearch = (userQuery?: string) => {
const path = getFilterUrl(this.props, { search: userQuery });
this.context.router.push(path);
this.props.onQueryChange({ search: userQuery });
};

render() {

+ 3
- 8
server/sonar-web/src/main/js/apps/projects/filters/SearchableFilterFooter.tsx View File

@@ -18,12 +18,12 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import * as PropTypes from 'prop-types';
import { getFilterUrl } from './utils';
import Select from '../../../components/controls/Select';
import { translate } from '../../../helpers/l10n';
import { RawQuery } from '../../../helpers/query';

interface Props {
onQueryChange: (change: RawQuery) => void;
property: string;
query: { [x: string]: any };
options: Array<{ label: string; value: string }>;
@@ -35,14 +35,9 @@ interface Props {
}

export default class SearchableFilterFooter extends React.PureComponent<Props> {
static contextTypes = {
router: PropTypes.object.isRequired
};

handleOptionChange = ({ value }: { value: string }) => {
const urlOptions = (this.props.query[this.props.property] || []).concat(value).join(',');
const path = getFilterUrl(this.props, { [this.props.property]: urlOptions });
this.context.router.push(path);
this.props.onQueryChange({ [this.props.property]: urlOptions });
};

render() {

+ 2
- 1
server/sonar-web/src/main/js/apps/projects/filters/SecurityFilter.tsx View File

@@ -22,13 +22,14 @@ import IssuesFilter from './IssuesFilter';
import { Facet } from '../types';
import VulnerabilityIcon from '../../../components/icons-components/VulnerabilityIcon';
import { translate } from '../../../helpers/l10n';
import { RawQuery } from '../../../helpers/query';

interface Props {
className?: string;
facet?: Facet;
headerDetail?: React.ReactNode;
isFavorite?: boolean;
maxFacetValue?: number;
onQueryChange: (change: RawQuery) => void;
organization?: { key: string };
query: { [x: string]: any };
value?: any;

+ 3
- 2
server/sonar-web/src/main/js/apps/projects/filters/SizeFilter.tsx View File

@@ -24,12 +24,13 @@ import SizeRating from '../../../components/ui/SizeRating';
import { translate } from '../../../helpers/l10n';
import { getSizeRatingLabel, getSizeRatingAverageValue } from '../../../helpers/ratings';
import { Facet } from '../types';
import { RawQuery } from '../../../helpers/query';

export interface Props {
className?: string;
facet?: Facet;
isFavorite?: boolean;
maxFacetValue?: number;
onQueryChange: (change: RawQuery) => void;
organization?: { key: string };
property?: string;
query: { [x: string]: any };
@@ -43,13 +44,13 @@ export default function SizeFilter(props: Props) {
<Filter
facet={props.facet}
maxFacetValue={props.maxFacetValue}
onQueryChange={props.onQueryChange}
value={props.value}
property={property}
className={props.className}
options={[1, 2, 3, 4, 5]}
query={props.query}
renderOption={renderOption}
isFavorite={props.isFavorite}
organization={props.organization}
getFacetValueForOption={getFacetValueForOption}
highlightUnder={1}

+ 4
- 3
server/sonar-web/src/main/js/apps/projects/filters/TagsFilter.tsx View File

@@ -26,11 +26,12 @@ import SearchableFilterOption from './SearchableFilterOption';
import { searchProjectTags } from '../../../api/components';
import { translate } from '../../../helpers/l10n';
import { Facet } from '../types';
import { RawQuery } from '../../../helpers/query';

interface Props {
facet?: Facet;
isFavorite?: boolean;
maxFacetValue?: number;
onQueryChange: (change: RawQuery) => void;
organization?: { key: string };
property?: string;
query: { [x: string]: any };
@@ -101,6 +102,7 @@ export default class TagsFilter extends React.PureComponent<Props, State> {

return (
<Filter
onQueryChange={this.props.onQueryChange}
property={property}
options={this.getSortedOptions(this.props.facet)}
query={this.props.query}
@@ -108,13 +110,12 @@ export default class TagsFilter extends React.PureComponent<Props, State> {
value={this.props.value}
facet={this.props.facet}
maxFacetValue={this.props.maxFacetValue}
isFavorite={this.props.isFavorite}
organization={this.props.organization}
getFacetValueForOption={this.getFacetValueForOption}
header={<FilterHeader name={translate('projects.facets.tags')} />}
footer={
<SearchableFilterFooter
isFavorite={this.props.isFavorite}
onQueryChange={this.props.onQueryChange}
isLoading={this.state.isLoading}
onInputChange={this.handleSearch}
onOpen={this.handleSearch}

+ 1
- 1
server/sonar-web/src/main/js/apps/projects/filters/__tests__/CoverageFilter-test.tsx View File

@@ -22,7 +22,7 @@ import { shallow } from 'enzyme';
import CoverageFilter from '../CoverageFilter';

it('renders', () => {
const wrapper = shallow(<CoverageFilter query={{}} />);
const wrapper = shallow(<CoverageFilter onQueryChange={jest.fn()} query={{}} />);
expect(wrapper).toMatchSnapshot();

const renderOption = wrapper.prop('renderOption');

+ 1
- 1
server/sonar-web/src/main/js/apps/projects/filters/__tests__/DuplicationsFilter-test.tsx View File

@@ -22,7 +22,7 @@ import { shallow } from 'enzyme';
import DuplicationsFilter from '../DuplicationsFilter';

it('renders', () => {
const wrapper = shallow(<DuplicationsFilter query={{}} />);
const wrapper = shallow(<DuplicationsFilter onQueryChange={jest.fn()} query={{}} />);
expect(wrapper).toMatchSnapshot();

const renderOption = wrapper.prop('renderOption');

+ 1
- 0
server/sonar-web/src/main/js/apps/projects/filters/__tests__/Filter-test.tsx View File

@@ -63,6 +63,7 @@ it('renders facet bar chart', () => {
function shallowRender(props?: any) {
return shallow(
<Filter
onQueryChange={jest.fn()}
options={[1, 2, 3]}
property="foo"
query={{}}

+ 3
- 1
server/sonar-web/src/main/js/apps/projects/filters/__tests__/IssuesFilter-test.tsx View File

@@ -22,7 +22,9 @@ import { shallow } from 'enzyme';
import IssuesFilter from '../IssuesFilter';

it('renders', () => {
const wrapper = shallow(<IssuesFilter name="bugs" property="bugs" query={{}} />);
const wrapper = shallow(
<IssuesFilter name="bugs" onQueryChange={jest.fn()} property="bugs" query={{}} />
);
expect(wrapper).toMatchSnapshot();

const renderOption = wrapper.prop('renderOption');

+ 8
- 3
server/sonar-web/src/main/js/apps/projects/filters/__tests__/LanguagesFilter-test.tsx View File

@@ -34,7 +34,12 @@ const languagesFacet = { java: 39, cs: 4, js: 1 };

it('should render the languages without the ones in the facet', () => {
const wrapper = shallow(
<LanguagesFilter facet={languagesFacet} languages={languages} query={{ languages: null }} />
<LanguagesFilter
facet={languagesFacet}
languages={languages}
onQueryChange={jest.fn()}
query={{ languages: null }}
/>
);
expect(wrapper).toMatchSnapshot();
});
@@ -43,8 +48,8 @@ it('should render the languages facet with the selected languages', () => {
const wrapper = shallow(
<LanguagesFilter
facet={languagesFacet}
isFavorite={true}
languages={languages}
onQueryChange={jest.fn()}
query={{ languages: ['java', 'cs'] }}
value={['java', 'cs']}
/>
@@ -69,8 +74,8 @@ it('should render maximum 10 languages in the searchbox results', () => {
const wrapper = shallow(
<LanguagesFilter
facet={{ ...languagesFacet, g: 1 }}
isFavorite={true}
languages={manyLanguages}
onQueryChange={jest.fn()}
query={{ languages: ['java', 'g'] }}
value={['java', 'g']}
/>

+ 1
- 1
server/sonar-web/src/main/js/apps/projects/filters/__tests__/MaintainabilityFilter-test.tsx View File

@@ -22,5 +22,5 @@ import { shallow } from 'enzyme';
import MaintainabilityFilter from '../MaintainabilityFilter';

it('renders', () => {
expect(shallow(<MaintainabilityFilter query={{}} />)).toMatchSnapshot();
expect(shallow(<MaintainabilityFilter onQueryChange={jest.fn()} query={{}} />)).toMatchSnapshot();
});

+ 1
- 1
server/sonar-web/src/main/js/apps/projects/filters/__tests__/NewCoverageFilter-test.tsx View File

@@ -22,5 +22,5 @@ import { shallow } from 'enzyme';
import NewCoverageFilter from '../NewCoverageFilter';

it('renders', () => {
expect(shallow(<NewCoverageFilter query={{}} />)).toMatchSnapshot();
expect(shallow(<NewCoverageFilter onQueryChange={jest.fn()} query={{}} />)).toMatchSnapshot();
});

+ 1
- 1
server/sonar-web/src/main/js/apps/projects/filters/__tests__/NewDuplicationsFilter-test.tsx View File

@@ -22,5 +22,5 @@ import { shallow } from 'enzyme';
import NewDuplicationsFilter from '../NewDuplicationsFilter';

it('renders', () => {
expect(shallow(<NewDuplicationsFilter query={{}} />)).toMatchSnapshot();
expect(shallow(<NewDuplicationsFilter onQueryChange={jest.fn()} query={{}} />)).toMatchSnapshot();
});

+ 1
- 1
server/sonar-web/src/main/js/apps/projects/filters/__tests__/NewLinesFilter-test.tsx View File

@@ -22,7 +22,7 @@ import { shallow } from 'enzyme';
import NewLinesFilter from '../NewLinesFilter';

it('renders', () => {
const wrapper = shallow(<NewLinesFilter query={{}} />);
const wrapper = shallow(<NewLinesFilter onQueryChange={jest.fn()} query={{}} />);
expect(wrapper).toMatchSnapshot();

const renderOption = wrapper.prop('renderOption');

+ 3
- 1
server/sonar-web/src/main/js/apps/projects/filters/__tests__/NewMaintainabilityFilter-test.tsx View File

@@ -22,5 +22,7 @@ import { shallow } from 'enzyme';
import NewMaintainabilityFilter from '../NewMaintainabilityFilter';

it('renders', () => {
expect(shallow(<NewMaintainabilityFilter query={{}} />)).toMatchSnapshot();
expect(
shallow(<NewMaintainabilityFilter onQueryChange={jest.fn()} query={{}} />)
).toMatchSnapshot();
});

+ 1
- 1
server/sonar-web/src/main/js/apps/projects/filters/__tests__/NewReliabilityFilter-test.tsx View File

@@ -22,5 +22,5 @@ import { shallow } from 'enzyme';
import NewReliabilityFilter from '../NewReliabilityFilter';

it('renders', () => {
expect(shallow(<NewReliabilityFilter query={{}} />)).toMatchSnapshot();
expect(shallow(<NewReliabilityFilter onQueryChange={jest.fn()} query={{}} />)).toMatchSnapshot();
});

+ 1
- 1
server/sonar-web/src/main/js/apps/projects/filters/__tests__/NewSecurityFilter-test.tsx View File

@@ -22,5 +22,5 @@ import { shallow } from 'enzyme';
import NewSecurityFilter from '../NewSecurityFilter';

it('renders', () => {
expect(shallow(<NewSecurityFilter query={{}} />)).toMatchSnapshot();
expect(shallow(<NewSecurityFilter onQueryChange={jest.fn()} query={{}} />)).toMatchSnapshot();
});

+ 1
- 1
server/sonar-web/src/main/js/apps/projects/filters/__tests__/QualityGateFilter-test.tsx View File

@@ -22,7 +22,7 @@ import { shallow } from 'enzyme';
import QualityGateFilter from '../QualityGateFilter';

it('renders', () => {
const wrapper = shallow(<QualityGateFilter query={{}} />);
const wrapper = shallow(<QualityGateFilter onQueryChange={jest.fn()} query={{}} />);
expect(wrapper).toMatchSnapshot();

const renderOption = wrapper.prop('renderOption');

+ 1
- 1
server/sonar-web/src/main/js/apps/projects/filters/__tests__/ReliabilityFilter-test.tsx View File

@@ -22,5 +22,5 @@ import { shallow } from 'enzyme';
import ReliabilityFilter from '../ReliabilityFilter';

it('renders', () => {
expect(shallow(<ReliabilityFilter query={{}} />)).toMatchSnapshot();
expect(shallow(<ReliabilityFilter onQueryChange={jest.fn()} query={{}} />)).toMatchSnapshot();
});

+ 5
- 3
server/sonar-web/src/main/js/apps/projects/filters/__tests__/SearchFilterContainer-test.tsx View File

@@ -22,9 +22,11 @@ import { shallow } from 'enzyme';
import SearchFilterContainer from '../SearchFilterContainer';

it('searches', () => {
const push = jest.fn();
const wrapper = shallow(<SearchFilterContainer query={{}} />, { context: { router: { push } } });
const onQueryChange = jest.fn();
const wrapper = shallow(<SearchFilterContainer onQueryChange={onQueryChange} query={{}} />, {
context: { router: { push: jest.fn() } }
});
expect(wrapper).toMatchSnapshot();
wrapper.find('SearchBox').prop<Function>('onChange')('foo');
expect(push).toBeCalledWith({ pathname: '/projects', query: { search: 'foo' } });
expect(onQueryChange).toBeCalledWith({ search: 'foo' });
});

+ 5
- 6
server/sonar-web/src/main/js/apps/projects/filters/__tests__/SearchableFilterFooter-test.tsx View File

@@ -30,6 +30,7 @@ const options = [
it('should render items without the ones in the facet', () => {
const wrapper = shallow(
<SearchableFilterFooter
onQueryChange={jest.fn()}
property="languages"
query={{ languages: ['java'] }}
options={options}
@@ -40,18 +41,16 @@ it('should render items without the ones in the facet', () => {
});

it('should render items without the ones in the facet', () => {
const push = jest.fn();
const onQueryChange = jest.fn();
const wrapper = shallow(
<SearchableFilterFooter
onQueryChange={onQueryChange}
property="languages"
query={{ languages: ['java'] }}
options={options}
/>,
{ context: { router: { push } } }
{ context: { router: { push: jest.fn() } } }
);
(wrapper.find('Select').prop('onChange') as Function)({ value: 'js' });
expect(push).toBeCalledWith({
pathname: '/projects',
query: { languages: 'java,js' }
});
expect(onQueryChange).toBeCalledWith({ languages: 'java,js' });
});

+ 1
- 1
server/sonar-web/src/main/js/apps/projects/filters/__tests__/SecurityFilter-test.tsx View File

@@ -22,5 +22,5 @@ import { shallow } from 'enzyme';
import SecurityFilter from '../SecurityFilter';

it('renders', () => {
expect(shallow(<SecurityFilter query={{}} />)).toMatchSnapshot();
expect(shallow(<SecurityFilter onQueryChange={jest.fn()} query={{}} />)).toMatchSnapshot();
});

+ 1
- 1
server/sonar-web/src/main/js/apps/projects/filters/__tests__/SizeFilter-test.tsx View File

@@ -22,7 +22,7 @@ import { shallow } from 'enzyme';
import SizeFilter from '../SizeFilter';

it('renders', () => {
const wrapper = shallow(<SizeFilter query={{}} />);
const wrapper = shallow(<SizeFilter onQueryChange={jest.fn()} query={{}} />);
expect(wrapper).toMatchSnapshot();

const renderOption = wrapper.prop('renderOption');

+ 5
- 3
server/sonar-web/src/main/js/apps/projects/filters/__tests__/TagsFilter-test.tsx View File

@@ -25,7 +25,9 @@ const tags = ['lang', 'sonar', 'csharp', 'dotnet', 'it', 'net'];
const tagsFacet = { lang: 4, sonar: 3, csharp: 1 };

it('should render the tags without the ones in the facet', () => {
const wrapper = shallow(<TagsFilter query={{ tags: null }} facet={tagsFacet} />);
const wrapper = shallow(
<TagsFilter onQueryChange={jest.fn()} query={{ tags: null }} facet={tagsFacet} />
);
expect(wrapper).toMatchSnapshot();
wrapper.setState({ tags });
expect(wrapper).toMatchSnapshot();
@@ -34,10 +36,10 @@ it('should render the tags without the ones in the facet', () => {
it('should render the tags facet with the selected tags', () => {
const wrapper = shallow(
<TagsFilter
onQueryChange={jest.fn()}
query={{ tags: ['lang', 'sonar'] }}
value={['lang', 'sonar']}
facet={tagsFacet}
isFavorite={true}
/>
);
expect(wrapper).toMatchSnapshot();
@@ -47,10 +49,10 @@ it('should render the tags facet with the selected tags', () => {
it('should render maximum 10 tags in the searchbox results', () => {
const wrapper = shallow(
<TagsFilter
onQueryChange={jest.fn()}
query={{ languages: ['java', 'ad'] }}
value={['java', 'ad']}
facet={{ ...tagsFacet, ad: 1 }}
isFavorite={true}
/>
);
wrapper.setState({ tags: [...tags, 'aa', 'ab', 'ac', 'ad', 'ae', 'af', 'ag', 'ah', 'ai'] });

+ 1
- 0
server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/CoverageFilter-test.tsx.snap View File

@@ -10,6 +10,7 @@ exports[`renders 1`] = `
}
highlightUnder={1}
highlightUnderMax={5}
onQueryChange={[Function]}
options={
Array [
1,

+ 1
- 0
server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/DuplicationsFilter-test.tsx.snap View File

@@ -10,6 +10,7 @@ exports[`renders 1`] = `
}
highlightUnder={1}
highlightUnderMax={5}
onQueryChange={[Function]}
options={
Array [
1,

+ 63
- 248
server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/Filter-test.tsx.snap View File

@@ -8,75 +8,48 @@ exports[`highlights under 1`] = `
<div
className="search-navigator-facet-list projects-facet-list"
>
<Link
<a
className="facet search-navigator-facet projects-facet"
data-key={1}
href="#"
key="1"
onClick={[Function]}
onlyActiveOnIndex={false}
style={Object {}}
to={
Object {
"pathname": "/projects",
"query": Object {
"foo": 1,
},
}
}
>
<span
className="facet-name"
>
1
</span>
</Link>
</a>
<div
className="search-navigator-facet-highlight-under-container"
>
<Link
<a
className="facet search-navigator-facet projects-facet"
data-key={2}
href="#"
key="2"
onClick={[Function]}
onlyActiveOnIndex={false}
style={Object {}}
to={
Object {
"pathname": "/projects",
"query": Object {
"foo": 2,
},
}
}
>
<span
className="facet-name"
>
2
</span>
</Link>
<Link
</a>
<a
className="facet search-navigator-facet projects-facet"
data-key={3}
href="#"
key="3"
onClick={[Function]}
onlyActiveOnIndex={false}
style={Object {}}
to={
Object {
"pathname": "/projects",
"query": Object {
"foo": 3,
},
}
}
>
<span
className="facet-name"
>
3
</span>
</Link>
</a>
</div>
</div>
</div>
@@ -90,73 +63,48 @@ exports[`hightlights under selected 1`] = `
<div
className="search-navigator-facet-list projects-facet-list"
>
<Link
<a
className="facet search-navigator-facet projects-facet"
data-key={1}
href="#"
key="1"
onClick={[Function]}
onlyActiveOnIndex={false}
style={Object {}}
to={
Object {
"pathname": "/projects",
"query": Object {
"foo": 1,
},
}
}
>
<span
className="facet-name"
>
1
</span>
</Link>
</a>
<div
className="search-navigator-facet-highlight-under-container"
>
<Link
<a
className="facet search-navigator-facet projects-facet active"
data-key={2}
href="#"
key="2"
onClick={[Function]}
onlyActiveOnIndex={false}
style={Object {}}
to={
Object {
"pathname": "/projects",
"query": Object {},
}
}
>
<span
className="facet-name"
>
2
</span>
</Link>
<Link
</a>
<a
className="facet search-navigator-facet projects-facet"
data-key={3}
href="#"
key="3"
onClick={[Function]}
onlyActiveOnIndex={false}
style={Object {}}
to={
Object {
"pathname": "/projects",
"query": Object {
"foo": 3,
},
}
}
>
<span
className="facet-name"
>
3
</span>
</Link>
</a>
</div>
</div>
</div>
@@ -170,72 +118,45 @@ exports[`renders 1`] = `
<div
className="search-navigator-facet-list projects-facet-list"
>
<Link
<a
className="facet search-navigator-facet projects-facet"
data-key={1}
href="#"
key="1"
onClick={[Function]}
onlyActiveOnIndex={false}
style={Object {}}
to={
Object {
"pathname": "/projects",
"query": Object {
"foo": 1,
},
}
}
>
<span
className="facet-name"
>
1
</span>
</Link>
<Link
</a>
<a
className="facet search-navigator-facet projects-facet"
data-key={2}
href="#"
key="2"
onClick={[Function]}
onlyActiveOnIndex={false}
style={Object {}}
to={
Object {
"pathname": "/projects",
"query": Object {
"foo": 2,
},
}
}
>
<span
className="facet-name"
>
2
</span>
</Link>
<Link
</a>
<a
className="facet search-navigator-facet projects-facet"
data-key={3}
href="#"
key="3"
onClick={[Function]}
onlyActiveOnIndex={false}
style={Object {}}
to={
Object {
"pathname": "/projects",
"query": Object {
"foo": 3,
},
}
}
>
<span
className="facet-name"
>
3
</span>
</Link>
</a>
</div>
</div>
`;
@@ -248,21 +169,12 @@ exports[`renders facet bar chart 1`] = `
<div
className="search-navigator-facet-list projects-facet-list"
>
<Link
<a
className="facet search-navigator-facet projects-facet"
data-key="a"
href="#"
key="a"
onClick={[Function]}
onlyActiveOnIndex={false}
style={Object {}}
to={
Object {
"pathname": "/projects",
"query": Object {
"foo": "a",
},
}
}
>
<span
className="facet-name"
@@ -286,22 +198,13 @@ exports[`renders facet bar chart 1`] = `
/>
</div>
</span>
</Link>
<Link
</a>
<a
className="facet search-navigator-facet projects-facet"
data-key="b"
href="#"
key="b"
onClick={[Function]}
onlyActiveOnIndex={false}
style={Object {}}
to={
Object {
"pathname": "/projects",
"query": Object {
"foo": "b",
},
}
}
>
<span
className="facet-name"
@@ -325,22 +228,13 @@ exports[`renders facet bar chart 1`] = `
/>
</div>
</span>
</Link>
<Link
</a>
<a
className="facet search-navigator-facet projects-facet"
data-key="c"
href="#"
key="c"
onClick={[Function]}
onlyActiveOnIndex={false}
style={Object {}}
to={
Object {
"pathname": "/projects",
"query": Object {
"foo": "c",
},
}
}
>
<span
className="facet-name"
@@ -364,7 +258,7 @@ exports[`renders facet bar chart 1`] = `
/>
</div>
</span>
</Link>
</a>
</div>
</div>
`;
@@ -378,72 +272,45 @@ exports[`renders header and footer 1`] = `
<div
className="search-navigator-facet-list projects-facet-list"
>
<Link
<a
className="facet search-navigator-facet projects-facet"
data-key={1}
href="#"
key="1"
onClick={[Function]}
onlyActiveOnIndex={false}
style={Object {}}
to={
Object {
"pathname": "/projects",
"query": Object {
"foo": 1,
},
}
}
>
<span
className="facet-name"
>
1
</span>
</Link>
<Link
</a>
<a
className="facet search-navigator-facet projects-facet"
data-key={2}
href="#"
key="2"
onClick={[Function]}
onlyActiveOnIndex={false}
style={Object {}}
to={
Object {
"pathname": "/projects",
"query": Object {
"foo": 2,
},
}
}
>
<span
className="facet-name"
>
2
</span>
</Link>
<Link
</a>
<a
className="facet search-navigator-facet projects-facet"
data-key={3}
href="#"
key="3"
onClick={[Function]}
onlyActiveOnIndex={false}
style={Object {}}
to={
Object {
"pathname": "/projects",
"query": Object {
"foo": 3,
},
}
}
>
<span
className="facet-name"
>
3
</span>
</Link>
</a>
</div>
<footer />
</div>
@@ -457,72 +324,45 @@ exports[`renders multiple selected 1`] = `
<div
className="search-navigator-facet-list projects-facet-list"
>
<Link
<a
className="facet search-navigator-facet projects-facet active"
data-key={1}
href="#"
key="1"
onClick={[Function]}
onlyActiveOnIndex={false}
style={Object {}}
to={
Object {
"pathname": "/projects",
"query": Object {
"foo": "2",
},
}
}
>
<span
className="facet-name"
>
1
</span>
</Link>
<Link
</a>
<a
className="facet search-navigator-facet projects-facet active"
data-key={2}
href="#"
key="2"
onClick={[Function]}
onlyActiveOnIndex={false}
style={Object {}}
to={
Object {
"pathname": "/projects",
"query": Object {
"foo": "1",
},
}
}
>
<span
className="facet-name"
>
2
</span>
</Link>
<Link
</a>
<a
className="facet search-navigator-facet projects-facet"
data-key={3}
href="#"
key="3"
onClick={[Function]}
onlyActiveOnIndex={false}
style={Object {}}
to={
Object {
"pathname": "/projects",
"query": Object {
"foo": "1,2,3",
},
}
}
>
<span
className="facet-name"
>
3
</span>
</Link>
</a>
</div>
</div>
`;
@@ -548,70 +388,45 @@ exports[`renders selected 1`] = `
<div
className="search-navigator-facet-list projects-facet-list"
>
<Link
<a
className="facet search-navigator-facet projects-facet"
data-key={1}
href="#"
key="1"
onClick={[Function]}
onlyActiveOnIndex={false}
style={Object {}}
to={
Object {
"pathname": "/projects",
"query": Object {
"foo": 1,
},
}
}
>
<span
className="facet-name"
>
1
</span>
</Link>
<Link
</a>
<a
className="facet search-navigator-facet projects-facet active"
data-key={2}
href="#"
key="2"
onClick={[Function]}
onlyActiveOnIndex={false}
style={Object {}}
to={
Object {
"pathname": "/projects",
"query": Object {},
}
}
>
<span
className="facet-name"
>
2
</span>
</Link>
<Link
</a>
<a
className="facet search-navigator-facet projects-facet"
data-key={3}
href="#"
key="3"
onClick={[Function]}
onlyActiveOnIndex={false}
style={Object {}}
to={
Object {
"pathname": "/projects",
"query": Object {
"foo": 3,
},
}
}
>
<span
className="facet-name"
>
3
</span>
</Link>
</a>
</div>
</div>
`;

+ 1
- 0
server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/IssuesFilter-test.tsx.snap View File

@@ -9,6 +9,7 @@ exports[`renders 1`] = `
/>
}
highlightUnder={1}
onQueryChange={[Function]}
options={
Array [
1,

+ 16
- 42
server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/LanguagesFilter-test.tsx.snap View File

@@ -12,7 +12,7 @@ exports[`should render maximum 10 languages in the searchbox results 1`] = `
}
footer={
<SearchableFilterFooter
isFavorite={true}
onQueryChange={[Function]}
options={
Array [
Object {
@@ -75,7 +75,7 @@ exports[`should render maximum 10 languages in the searchbox results 1`] = `
name="projects.facets.languages"
/>
}
isFavorite={true}
onQueryChange={[Function]}
options={
Array [
"java",
@@ -114,7 +114,7 @@ exports[`should render the languages facet with the selected languages 1`] = `
}
footer={
<SearchableFilterFooter
isFavorite={true}
onQueryChange={[Function]}
options={
Array [
Object {
@@ -149,7 +149,7 @@ exports[`should render the languages facet with the selected languages 1`] = `
name="projects.facets.languages"
/>
}
isFavorite={true}
onQueryChange={[Function]}
options={
Array [
"java",
@@ -187,21 +187,12 @@ exports[`should render the languages facet with the selected languages 2`] = `
<div
className="search-navigator-facet-list projects-facet-list"
>
<Link
<a
className="facet search-navigator-facet projects-facet active"
data-key="java"
href="#"
key="java"
onClick={[Function]}
onlyActiveOnIndex={false}
style={Object {}}
to={
Object {
"pathname": "/projects/favorite",
"query": Object {
"languages": "cs",
},
}
}
>
<span
className="facet-name"
@@ -221,22 +212,13 @@ exports[`should render the languages facet with the selected languages 2`] = `
>
39
</span>
</Link>
<Link
</a>
<a
className="facet search-navigator-facet projects-facet active"
data-key="cs"
href="#"
key="cs"
onClick={[Function]}
onlyActiveOnIndex={false}
style={Object {}}
to={
Object {
"pathname": "/projects/favorite",
"query": Object {
"languages": "java",
},
}
}
>
<span
className="facet-name"
@@ -256,22 +238,13 @@ exports[`should render the languages facet with the selected languages 2`] = `
>
4
</span>
</Link>
<Link
</a>
<a
className="facet search-navigator-facet projects-facet"
data-key="js"
href="#"
key="js"
onClick={[Function]}
onlyActiveOnIndex={false}
style={Object {}}
to={
Object {
"pathname": "/projects/favorite",
"query": Object {
"languages": "java,cs,js",
},
}
}
>
<span
className="facet-name"
@@ -291,10 +264,10 @@ exports[`should render the languages facet with the selected languages 2`] = `
>
1
</span>
</Link>
</a>
</div>
<SearchableFilterFooter
isFavorite={true}
onQueryChange={[Function]}
options={
Array [
Object {
@@ -335,7 +308,7 @@ exports[`should render the languages without the ones in the facet 1`] = `
}
footer={
<SearchableFilterFooter
isFavorite={undefined}
onQueryChange={[Function]}
options={
Array [
Object {
@@ -367,6 +340,7 @@ exports[`should render the languages without the ones in the facet 1`] = `
name="projects.facets.languages"
/>
}
onQueryChange={[Function]}
options={
Array [
"java",

+ 1
- 0
server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/MaintainabilityFilter-test.tsx.snap View File

@@ -15,6 +15,7 @@ exports[`renders 1`] = `
</span>
}
name="Maintainability"
onQueryChange={[Function]}
property="maintainability"
query={Object {}}
/>

+ 1
- 0
server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/NewCoverageFilter-test.tsx.snap View File

@@ -3,6 +3,7 @@
exports[`renders 1`] = `
<CoverageFilter
className="leak-facet-box"
onQueryChange={[Function]}
property="new_coverage"
query={Object {}}
/>

+ 1
- 0
server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/NewDuplicationsFilter-test.tsx.snap View File

@@ -3,6 +3,7 @@
exports[`renders 1`] = `
<DuplicationsFilter
className="leak-facet-box"
onQueryChange={[Function]}
property="new_duplications"
query={Object {}}
/>

+ 1
- 0
server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/NewLinesFilter-test.tsx.snap View File

@@ -10,6 +10,7 @@ exports[`renders 1`] = `
/>
}
highlightUnder={1}
onQueryChange={[Function]}
options={
Array [
1,

+ 1
- 0
server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/NewMaintainabilityFilter-test.tsx.snap View File

@@ -16,6 +16,7 @@ exports[`renders 1`] = `
</span>
}
name="Maintainability"
onQueryChange={[Function]}
property="new_maintainability"
query={Object {}}
/>

+ 1
- 0
server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/NewReliabilityFilter-test.tsx.snap View File

@@ -16,6 +16,7 @@ exports[`renders 1`] = `
</span>
}
name="Reliability"
onQueryChange={[Function]}
property="new_reliability"
query={Object {}}
/>

+ 1
- 0
server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/NewSecurityFilter-test.tsx.snap View File

@@ -16,6 +16,7 @@ exports[`renders 1`] = `
</span>
}
name="Security"
onQueryChange={[Function]}
property="new_security"
query={Object {}}
/>

+ 1
- 0
server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/QualityGateFilter-test.tsx.snap View File

@@ -8,6 +8,7 @@ exports[`renders 1`] = `
name="projects.facets.quality_gate"
/>
}
onQueryChange={[Function]}
options={
Array [
"OK",

+ 1
- 0
server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/ReliabilityFilter-test.tsx.snap View File

@@ -15,6 +15,7 @@ exports[`renders 1`] = `
</span>
}
name="Reliability"
onQueryChange={[Function]}
property="reliability"
query={Object {}}
/>

+ 1
- 0
server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/SecurityFilter-test.tsx.snap View File

@@ -15,6 +15,7 @@ exports[`renders 1`] = `
</span>
}
name="Security"
onQueryChange={[Function]}
property="security"
query={Object {}}
/>

+ 1
- 0
server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/SizeFilter-test.tsx.snap View File

@@ -9,6 +9,7 @@ exports[`renders 1`] = `
/>
}
highlightUnder={1}
onQueryChange={[Function]}
options={
Array [
1,

+ 18
- 43
server/sonar-web/src/main/js/apps/projects/filters/__tests__/__snapshots__/TagsFilter-test.tsx.snap View File

@@ -12,10 +12,10 @@ exports[`should render maximum 10 tags in the searchbox results 1`] = `
}
footer={
<SearchableFilterFooter
isFavorite={true}
isLoading={false}
onInputChange={[Function]}
onOpen={[Function]}
onQueryChange={[Function]}
options={
Array [
Object {
@@ -78,7 +78,7 @@ exports[`should render maximum 10 tags in the searchbox results 1`] = `
name="projects.facets.tags"
/>
}
isFavorite={true}
onQueryChange={[Function]}
options={
Array [
"lang",
@@ -117,10 +117,10 @@ exports[`should render the tags facet with the selected tags 1`] = `
}
footer={
<SearchableFilterFooter
isFavorite={true}
isLoading={false}
onInputChange={[Function]}
onOpen={[Function]}
onQueryChange={[Function]}
options={Array []}
organization={undefined}
property="tags"
@@ -140,7 +140,7 @@ exports[`should render the tags facet with the selected tags 1`] = `
name="projects.facets.tags"
/>
}
isFavorite={true}
onQueryChange={[Function]}
options={
Array [
"lang",
@@ -178,21 +178,12 @@ exports[`should render the tags facet with the selected tags 2`] = `
<div
className="search-navigator-facet-list projects-facet-list"
>
<Link
<a
className="facet search-navigator-facet projects-facet active"
data-key="lang"
href="#"
key="lang"
onClick={[Function]}
onlyActiveOnIndex={false}
style={Object {}}
to={
Object {
"pathname": "/projects/favorite",
"query": Object {
"tags": "sonar",
},
}
}
>
<span
className="facet-name"
@@ -206,22 +197,13 @@ exports[`should render the tags facet with the selected tags 2`] = `
>
4
</span>
</Link>
<Link
</a>
<a
className="facet search-navigator-facet projects-facet active"
data-key="sonar"
href="#"
key="sonar"
onClick={[Function]}
onlyActiveOnIndex={false}
style={Object {}}
to={
Object {
"pathname": "/projects/favorite",
"query": Object {
"tags": "lang",
},
}
}
>
<span
className="facet-name"
@@ -235,22 +217,13 @@ exports[`should render the tags facet with the selected tags 2`] = `
>
3
</span>
</Link>
<Link
</a>
<a
className="facet search-navigator-facet projects-facet"
data-key="csharp"
href="#"
key="csharp"
onClick={[Function]}
onlyActiveOnIndex={false}
style={Object {}}
to={
Object {
"pathname": "/projects/favorite",
"query": Object {
"tags": "lang,sonar,csharp",
},
}
}
>
<span
className="facet-name"
@@ -264,13 +237,13 @@ exports[`should render the tags facet with the selected tags 2`] = `
>
1
</span>
</Link>
</a>
</div>
<SearchableFilterFooter
isFavorite={true}
isLoading={false}
onInputChange={[Function]}
onOpen={[Function]}
onQueryChange={[Function]}
options={Array []}
property="tags"
query={
@@ -296,10 +269,10 @@ exports[`should render the tags without the ones in the facet 1`] = `
}
footer={
<SearchableFilterFooter
isFavorite={undefined}
isLoading={false}
onInputChange={[Function]}
onOpen={[Function]}
onQueryChange={[Function]}
options={Array []}
organization={undefined}
property="tags"
@@ -316,6 +289,7 @@ exports[`should render the tags without the ones in the facet 1`] = `
name="projects.facets.tags"
/>
}
onQueryChange={[Function]}
options={
Array [
"lang",
@@ -344,10 +318,10 @@ exports[`should render the tags without the ones in the facet 2`] = `
}
footer={
<SearchableFilterFooter
isFavorite={undefined}
isLoading={false}
onInputChange={[Function]}
onOpen={[Function]}
onQueryChange={[Function]}
options={
Array [
Object {
@@ -379,6 +353,7 @@ exports[`should render the tags without the ones in the facet 2`] = `
name="projects.facets.tags"
/>
}
onQueryChange={[Function]}
options={
Array [
"lang",

+ 0
- 8
server/sonar-web/src/main/js/apps/projects/styles.css View File

@@ -1,11 +1,3 @@
.projects-page-side {
transition: top 150ms ease-out;
}

.projects-page-content {
transition: padding-top 150ms ease-out;
}

.projects-topbar-items {
display: flex;
align-items: center;

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

@@ -60,6 +60,7 @@ download_verb=Download
duplications=Duplications
edit=Edit
events=Events
explore=Explore
false=False
favorite=Favorite
file=File
@@ -605,6 +606,7 @@ issues.to_navigate_issue_locations=to navigate issue locations
issues.to_switch_flows=to switch flows
issues.leak_period=Leak Period
issues.my_issues=My Issues
issues.no_my_issues=There are no issues assigned to you.


#------------------------------------------------------------------------------

Loading…
Cancel
Save